Skip to content

feat: migrate module to Nuxt 4 and add Tailwind v4 support#1280

Open
Jeanspkdz wants to merge 4 commits intopingdotgg:mainfrom
Jeanspkdz:feat/nuxt4-support
Open

feat: migrate module to Nuxt 4 and add Tailwind v4 support#1280
Jeanspkdz wants to merge 4 commits intopingdotgg:mainfrom
Jeanspkdz:feat/nuxt4-support

Conversation

@Jeanspkdz
Copy link
Copy Markdown

@Jeanspkdz Jeanspkdz commented Apr 29, 2026

Summary

This PR migrates the Nuxt UploadThing module to Nuxt 4 and adds Tailwindv4 support

What changed

  • Upgraded Nuxt module dependencies for Nuxt 4 compatibility.
  • Refactored module setup to generate runtime components, helpers, and server handler from the user’s router export.
  • Added configurable options for:
    • fileRouterPath
    • fileRouterExport
    • componentPrefix
    • useTailwindStyles
    • routeHandlerConfig
  • Added Tailwind v4 support via generated CSS source registration.
  • Fallback to UploadThing default CSS when Tailwind is not available.
  • Removed postbuild.ts workaround.
  • Rewrote packages/nuxt/README.md with updated setup instructions.

Breaking Changes

  • Requires Nuxt 4+

Summary by CodeRabbit

  • New Features

    • Migration to Nuxt 4 and Tailwind v4 support with automatic Tailwind detection and generated styling asset.
  • Breaking Changes

    • ESM-first package exports and updated build outputs.
    • Module configuration shape changed; generated component/composable integration and runtime API wiring reworked or removed.
  • Documentation

    • Major README rewrite with new quick start, examples, module options, and Tailwind styling instructions.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 29, 2026

🦋 Changeset detected

Latest commit: 12dd75c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@uploadthing/nuxt Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 29, 2026

@Jeanspkdz is attempting to deploy a commit to the Ping Labs Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 29, 2026

Walkthrough

Migrates @uploadthing/nuxt to Nuxt 4 and Tailwind v4, switches packaging to ESM-first, replaces static runtime components/helpers with build-time generated artifacts, updates module API/options and runtime wiring, and removes the post-build type-patching script.

Changes

Cohort / File(s) Summary
Release & Docs
/.changeset/beige-camels-shout.md, packages/nuxt/README.md
Adds a major release changeset (Nuxt 4 + Tailwind v4) and rewrites the README with new badges, examples, Tailwind v4 instructions, and updated component API usage.
Config & Packaging
packages/nuxt/.nuxtrc, packages/nuxt/eslint.config.js, packages/nuxt/tsconfig.json, packages/nuxt/package.json
Moves TS bundler flag from experimentalfuture, adds parser project options for ESLint, refines tsconfig includes/excludes, converts package to ESM-first exports (MJS/types.d.mts), adds style export and typesVersions, updates scripts/deps/peerDependencies.
Removed postbuild
packages/nuxt/postbuild.ts
Deletes post-build script that patched generated .d.ts files.
Module core rewrite
packages/nuxt/src/module.ts
Redesigns ModuleOptions (fileRouterPath/export, useTailwindStyles, componentPrefix, routeHandlerConfig), uses .with({ defaults }), resolves/imports router file, merges runtime config, aliases #ut-router, detects Tailwind v3/v4, generates typed components/helpers and runtime API handler, and updates styling registration logic.
Runtime components & helpers removed
packages/nuxt/src/runtime/components/button.ts, packages/nuxt/src/runtime/components/dropzone.ts, packages/nuxt/src/runtime/composables/useUploadThing.ts
Removes static UploadThing Vue components and the typed composable — these are now generated by the module at build time.
Server & router changes
packages/nuxt/src/runtime/server/api/uploadthing.ts, packages/nuxt/src/runtime/server/router.ts
Deletes the static API handler (now generated); minor formatting/quote/semicolon adjustments in router file.
Styling placeholder
packages/nuxt/src/runtime/uploadthing-tw.css
Adds an intentionally-empty Tailwind v4 placeholder CSS (comments only) used by the module’s Tailwind integration logic.

Sequence Diagram(s)

sequenceDiagram
  participant Dev as Developer (build/dev)
  participant NuxtModule as Nuxt Module
  participant FileRouter as User fileRouter
  participant Generator as Build-time Generator
  participant NuxtRuntime as Nuxt Runtime

  Dev->>NuxtModule: run nuxt dev/build
  NuxtModule->>FileRouter: resolve & import `fileRouterPath`/`fileRouterExport`
  FileRouter-->>NuxtModule: router definitions (types/exports)
  NuxtModule->>Generator: generate typed components, helpers, API handler, CSS
  Generator-->>NuxtModule: emit artifacts (TS/MJS/CSS)
  NuxtModule->>NuxtRuntime: register components, alias `#ut-router`, inject runtime config, expose generated /api/uploadthing
  NuxtRuntime-->>Dev: runtime with generated components + API handler available
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately and specifically describes the main changes: migration to Nuxt 4 and Tailwind v4 support, which align directly with the changeset objectives.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 29, 2026

Greptile Summary

This PR migrates @uploadthing/nuxt to Nuxt 4, replaces the static component/composable approach with code-generated typed artifacts, adds Tailwind v4 support, and removes the postbuild.ts workaround.

  • Unstyled components with Tailwind v3: when useTailwindStyles: true and Tailwind v3 is detected, applyUploadthingStyles returns without applying any CSS — no Tailwind styles, no default CSS fallback. Every other error branch falls back to registerUploadthingCss(), but this branch does not.
  • Wrong package name in README: install instructions and badge URLs reference nuxt-uploadthing, but package.json still declares \"name\": \"@uploadthing/nuxt\" and the changeset targets that same name.

Confidence Score: 3/5

Not safe to merge without addressing the two P1 issues: missing CSS fallback and wrong package name in docs.

Two P1 defects: the Tailwind v3 path leaves components completely unstyled in production, and the README install command points to a non-existent package name, making the docs immediately broken for new users.

packages/nuxt/src/module.ts (Tailwind v3 CSS fallback) and packages/nuxt/README.md (package name).

Important Files Changed

Filename Overview
packages/nuxt/src/module.ts Complete rewrite for Nuxt 4; introduces two bugs: no CSS fallback when useTailwindStyles+Tailwind v3, and createRouteHandler called per-request. Leftover commented code.
packages/nuxt/README.md Rewritten docs reference nuxt-uploadthing package name which conflicts with the actual package name @uploadthing/nuxt in package.json.
packages/nuxt/package.json Dependency upgrades to Nuxt 4 (@nuxt/kit ^4, nuxt ^4, module-builder ^1); removed CJS output and postbuild step; added pathe and @nuxt/test-utils.
packages/nuxt/src/runtime/server/router.ts Fallback empty router stub; straightforward and correct.
.changeset/beige-camels-shout.md Correct major-bump changeset for the breaking Nuxt 4 migration.
packages/nuxt/src/runtime/uploadthing-tw.css Placeholder CSS file that is overwritten at build time by the module's addTemplate calls; acts as a source-tree sentinel.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Nuxt Module Setup] --> B[Resolve fileRouterPath]
    B --> C{Router file exists?}
    C -- No --> D[Alias #ut-router → empty stub]
    C -- Yes --> E[Alias #ut-router → user file]
    D & E --> F[applyUploadthingStyles]
    F --> G{useTailwindStyles?}
    G -- false --> H[registerUploadthingCss\npush @uploadthing/vue/styles.css]
    G -- true --> I[getTailwindVersion]
    I --> J{Version?}
    J -- null --> K[warn + registerUploadthingCss]
    J -- 3 --> L[warn only ⚠️ no CSS applied]
    J -- 4 --> M[resolveUploadthingVueDist]
    M --> N{dist found?}
    N -- No --> O[warn + registerUploadthingCss]
    N -- Yes --> P[registerUploadthingTailwindCss\n@import uploadthing/tw/v4]
    F --> Q[generateUploadthingArtifacts]
    Q --> R[addTemplate upload-button.ts]
    Q --> S[addTemplate upload-dropzone.ts]
    Q --> T[addTemplate upload-helpers.ts]
    Q --> U[addTemplate server handler]
    U --> V[addServerHandler /api/uploadthing]
    Q --> W[addComponentsDir runtime/components]
    Q --> X[addImportsDir runtime/utils]
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: packages/nuxt/src/module.ts
Line: 155-160

Comment:
**Missing CSS fallback for Tailwind v3**

When `useTailwindStyles: true` and Tailwind v3 is detected, the function logs a warning and returns without applying *any* CSS. Components will be completely unstyled unless the user also manually sets up `withUt`. The other two error branches both call `registerUploadthingCss()` as a fallback, but this branch does not.

```suggestion
  if (tailwindVersion === '3') {
    logger.warn(
      '[nuxt-uploadthing] Tailwind CSS v3 detected. To enable UploadThing Tailwind integration, wrap your Tailwind config with `withUt` from "uploadthing/tw". See: https://docs.uploadthing.com/concepts/theming#configuring-tailwind-css. Falling back to default UploadThing CSS.',
    )
    return registerUploadthingCss()
  }
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: packages/nuxt/README.md
Line: 69-71

Comment:
**Wrong package name in install instructions**

The README tells users to install `nuxt-uploadthing`, but `package.json` still declares `"name": "@uploadthing/nuxt"` and the changeset also references `@uploadthing/nuxt`. The badges at the top also link to the `nuxt-uploadthing` npm slug, which would 404 unless this package is being renamed (but the package.json has not changed its `name` field).

```suggestion
pnpm add @uploadthing/nuxt uploadthing
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: packages/nuxt/src/module.ts
Line: 316-339

Comment:
**`createRouteHandler` reconstructed on every request**

`createRouteHandler(...)` is called inside `defineEventHandler`, so a new handler is instantiated for every incoming HTTP request. This is wasteful (potential connection pooling, middleware setup, etc. repeated per request). The handler should be created once outside the event handler and reused.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: packages/nuxt/src/module.ts
Line: 113-117

Comment:
**Leftover commented-out code**

Two stale comments remain in the module — `// const _require = createRequire(import.meta.url)` here and `// const relativeDist = ...` / `// return relativeDist` in `resolveUploadthingVueDist`. These should be removed before merging.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "feat: migrate module to Nuxt 4 and add T..." | Re-trigger Greptile

Comment on lines +155 to +160
if (tailwindVersion === '3') {
logger.warn(
'[nuxt-uploadthing] Tailwind CSS v3 detected. To enable UploadThing Tailwind integration, wrap your Tailwind config with `withUt` from "uploadthing/tw". See: https://docs.uploadthing.com/concepts/theming#configuring-tailwind-css.',
)
return
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Missing CSS fallback for Tailwind v3

When useTailwindStyles: true and Tailwind v3 is detected, the function logs a warning and returns without applying any CSS. Components will be completely unstyled unless the user also manually sets up withUt. The other two error branches both call registerUploadthingCss() as a fallback, but this branch does not.

Suggested change
if (tailwindVersion === '3') {
logger.warn(
'[nuxt-uploadthing] Tailwind CSS v3 detected. To enable UploadThing Tailwind integration, wrap your Tailwind config with `withUt` from "uploadthing/tw". See: https://docs.uploadthing.com/concepts/theming#configuring-tailwind-css.',
)
return
}
if (tailwindVersion === '3') {
logger.warn(
'[nuxt-uploadthing] Tailwind CSS v3 detected. To enable UploadThing Tailwind integration, wrap your Tailwind config with `withUt` from "uploadthing/tw". See: https://docs.uploadthing.com/concepts/theming#configuring-tailwind-css. Falling back to default UploadThing CSS.',
)
return registerUploadthingCss()
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/nuxt/src/module.ts
Line: 155-160

Comment:
**Missing CSS fallback for Tailwind v3**

When `useTailwindStyles: true` and Tailwind v3 is detected, the function logs a warning and returns without applying *any* CSS. Components will be completely unstyled unless the user also manually sets up `withUt`. The other two error branches both call `registerUploadthingCss()` as a fallback, but this branch does not.

```suggestion
  if (tailwindVersion === '3') {
    logger.warn(
      '[nuxt-uploadthing] Tailwind CSS v3 detected. To enable UploadThing Tailwind integration, wrap your Tailwind config with `withUt` from "uploadthing/tw". See: https://docs.uploadthing.com/concepts/theming#configuring-tailwind-css. Falling back to default UploadThing CSS.',
    )
    return registerUploadthingCss()
  }
```

How can I resolve this? If you propose a fix, please make it concise.

Comment thread packages/nuxt/README.md
Comment on lines 69 to 71

2. Mount a component in your app and start uploading files:
3) Use generated components anywhere in your app

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Wrong package name in install instructions

The README tells users to install nuxt-uploadthing, but package.json still declares "name": "@uploadthing/nuxt" and the changeset also references @uploadthing/nuxt. The badges at the top also link to the nuxt-uploadthing npm slug, which would 404 unless this package is being renamed (but the package.json has not changed its name field).

Suggested change
2. Mount a component in your app and start uploading files:
3) Use generated components anywhere in your app
pnpm add @uploadthing/nuxt uploadthing
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/nuxt/README.md
Line: 69-71

Comment:
**Wrong package name in install instructions**

The README tells users to install `nuxt-uploadthing`, but `package.json` still declares `"name": "@uploadthing/nuxt"` and the changeset also references `@uploadthing/nuxt`. The badges at the top also link to the `nuxt-uploadthing` npm slug, which would 404 unless this package is being renamed (but the package.json has not changed its `name` field).

```suggestion
pnpm add @uploadthing/nuxt uploadthing
```

How can I resolve this? If you propose a fix, please make it concise.

Comment thread packages/nuxt/src/module.ts Outdated
Comment on lines +316 to +339

const emptyStringToUndefined = (obj) => {
const next = {}
for (const key in obj) {
next[key] = obj[key] === '' ? undefined : obj[key]
}
return next
}

export default defineEventHandler((event) => {
const runtime = useRuntimeConfig()
const config = emptyStringToUndefined(runtime.uploadthing?.routeHandlerConfig ?? {})
const hasConfig = Object.keys(config).length > 0


const router = RouterModule[ROUTER_EXPORT]
if (!router) {
throw new Error('[nuxt-uploadthing] Router export not found: ' + ROUTER_EXPORT)
}

return createRouteHandler({
router,
...(hasConfig ? { config } : {}),
})(event)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 createRouteHandler reconstructed on every request

createRouteHandler(...) is called inside defineEventHandler, so a new handler is instantiated for every incoming HTTP request. This is wasteful (potential connection pooling, middleware setup, etc. repeated per request). The handler should be created once outside the event handler and reused.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/nuxt/src/module.ts
Line: 316-339

Comment:
**`createRouteHandler` reconstructed on every request**

`createRouteHandler(...)` is called inside `defineEventHandler`, so a new handler is instantiated for every incoming HTTP request. This is wasteful (potential connection pooling, middleware setup, etc. repeated per request). The handler should be created once outside the event handler and reused.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +113 to +117
function getTailwindVersion(): '3' | '4' | null {
try {
const nuxt = useNuxt()
// const _require = createRequire(import.meta.url)
const _require = createRequire(nuxt.options.rootDir + '/')
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Leftover commented-out code

Two stale comments remain in the module — // const _require = createRequire(import.meta.url) here and // const relativeDist = ... / // return relativeDist in resolveUploadthingVueDist. These should be removed before merging.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/nuxt/src/module.ts
Line: 113-117

Comment:
**Leftover commented-out code**

Two stale comments remain in the module — `// const _require = createRequire(import.meta.url)` here and `// const relativeDist = ...` / `// return relativeDist` in `resolveUploadthingVueDist`. These should be removed before merging.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/nuxt/package.json (1)

58-60: ⚠️ Potential issue | 🟠 Major

Add Nuxt to peerDependencies to match framework integration pattern.

All other framework-specific packages in this workspace declare their framework as a peer dependency (@uploadthing/react, @uploadthing/vue, @uploadthing/svelte, @uploadthing/solid, @uploadthing/expo). The Nuxt package should do the same, especially since it:

  • Imports Nuxt runtime APIs (defineNuxtModule, useNuxt) from @nuxt/kit in its dependencies
  • Just migrated to Nuxt 4+ as a breaking change
  • Needs to signal the Nuxt version requirement at install time
Proposed fix
   "peerDependencies": {
+    "nuxt": "^4.0.0",
     "uploadthing": "^7.2.0"
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/nuxt/package.json` around lines 58 - 60, The Nuxt package.json is
missing Nuxt as a peerDependency; update the "peerDependencies" block in
package.json to declare "nuxt" with an appropriate range (e.g. "^4.0.0" or ">=4
<5") so installs will warn about the required Nuxt major; this aligns with other
framework packages and the fact the code imports Nuxt runtime APIs
(defineNuxtModule, useNuxt) from `@nuxt/kit` and was migrated to Nuxt 4+. Ensure
the peerDependencies entry is added alongside "uploadthing" under the existing
peerDependencies object.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/nuxt/README.md`:
- Around line 41-42: The docs show inconsistent casing for the componentPrefix
option: replace the lowercase variant with the exact casing used elsewhere so
examples match; update occurrences of componentPrefix values that read
"Uploadthing" to "UploadThing" (search for the symbol componentPrefix and the
example values to locate and unify them) so generated component names described
in the README are consistent across examples.
- Around line 25-26: Replace all instances of the incorrect package identifier
"nuxt-uploadthing" with the canonical scoped name "@uploadthing/nuxt" throughout
the README: update the install command to "pnpm add `@uploadthing/nuxt`
uploadthing", fix module config examples and any comments referencing
"nuxt-uploadthing", and update npm badge URLs/text to point to the scoped
package "@uploadthing/nuxt" so examples and links match the package manifest.

In `@packages/nuxt/src/module.ts`:
- Around line 195-202: registerUploadthingTailwindCss currently generates
uploadthing-tw.css via addTemplate but never registers it with Nuxt's CSS
pipeline; after the addTemplate call in registerUploadthingTailwindCss, push the
generated CSS into nuxt.options.css (e.g.
nuxt.options.css.push(resolver.resolve('./runtime/uploadthing-tw.css'))),
mirroring the pattern used in registerUploadthingCss (which calls
nuxt.options.css.push('@uploadthing/vue/styles.css')) so the Tailwind v4
stylesheet is actually loaded by Nuxt.
- Around line 231-235: The generated artifacts (upload-button, dropzone,
helpers, CSS, and import entries) are being written directly into the package
source via dst: resolver.resolve('./runtime/...'), which breaks immutable
installs; instead, call addTemplate(...) without forcing a src package path,
capture the returned template objects (e.g., the result of addTemplate for
'upload-button.ts' and other files), then use each template.dst value when
registering components/imports (mirror the server handler pattern used around
the server handler code that already uses template.dst); update usages in
addComponentsDir, addImportsDir and CSS registration to reference those
template.dst paths rather than resolver.resolve('./runtime/...') so Nuxt emits
into its build output directory.

---

Outside diff comments:
In `@packages/nuxt/package.json`:
- Around line 58-60: The Nuxt package.json is missing Nuxt as a peerDependency;
update the "peerDependencies" block in package.json to declare "nuxt" with an
appropriate range (e.g. "^4.0.0" or ">=4 <5") so installs will warn about the
required Nuxt major; this aligns with other framework packages and the fact the
code imports Nuxt runtime APIs (defineNuxtModule, useNuxt) from `@nuxt/kit` and
was migrated to Nuxt 4+. Ensure the peerDependencies entry is added alongside
"uploadthing" under the existing peerDependencies object.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0ffe732f-77a3-4b19-9333-181b32ef60e6

📥 Commits

Reviewing files that changed from the base of the PR and between 720849f and 724a45d.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (14)
  • .changeset/beige-camels-shout.md
  • packages/nuxt/.nuxtrc
  • packages/nuxt/README.md
  • packages/nuxt/eslint.config.js
  • packages/nuxt/package.json
  • packages/nuxt/postbuild.ts
  • packages/nuxt/src/module.ts
  • packages/nuxt/src/runtime/components/button.ts
  • packages/nuxt/src/runtime/components/dropzone.ts
  • packages/nuxt/src/runtime/composables/useUploadThing.ts
  • packages/nuxt/src/runtime/server/api/uploadthing.ts
  • packages/nuxt/src/runtime/server/router.ts
  • packages/nuxt/src/runtime/uploadthing-tw.css
  • packages/nuxt/tsconfig.json
💤 Files with no reviewable changes (5)
  • packages/nuxt/src/runtime/composables/useUploadThing.ts
  • packages/nuxt/src/runtime/components/button.ts
  • packages/nuxt/src/runtime/server/api/uploadthing.ts
  • packages/nuxt/src/runtime/components/dropzone.ts
  • packages/nuxt/postbuild.ts

Comment thread packages/nuxt/README.md Outdated
Comment thread packages/nuxt/README.md Outdated
Comment on lines +195 to +202
function registerUploadthingTailwindCss(distPath: string) {
addTemplate({
write: true,
filename: 'uploadthing-tw.css',
dst: resolver.resolve('./runtime/uploadthing-tw.css'),
getContents: () =>
`@import "uploadthing/tw/v4";\n@source "${distPath}";\n`,
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the actual file and the specific lines mentioned
cd /tmp/repo && head -250 packages/nuxt/src/module.ts | tail -100

Repository: pingdotgg/uploadthing

Length of output: 125


🏁 Script executed:

# Get the complete context around the registerUploadthingTailwindCss function
cat -n packages/nuxt/src/module.ts | sed -n '190,210p'

Repository: pingdotgg/uploadthing

Length of output: 817


🏁 Script executed:

# Search for registerUploadthingCss to see how it handles CSS registration (for comparison)
rg -A 10 "function registerUploadthingCss" packages/nuxt/src/module.ts

Repository: pingdotgg/uploadthing

Length of output: 389


🏁 Script executed:

# Search for useTailwindStyles to understand the flow
rg -B 5 -A 10 "useTailwindStyles" packages/nuxt/src/module.ts

Repository: pingdotgg/uploadthing

Length of output: 1941


🏁 Script executed:

# Check if addTemplate is used elsewhere with CSS registration
rg -B 2 -A 5 "nuxt.options.css" packages/nuxt/src/module.ts

Repository: pingdotgg/uploadthing

Length of output: 249


🏁 Script executed:

# Let's see the complete applyUploadthingStyles function to understand the full flow
rg -A 30 "function applyUploadthingStyles" packages/nuxt/src/module.ts

Repository: pingdotgg/uploadthing

Length of output: 1171


🏁 Script executed:

# Check if registerUploadthingTailwindCss is actually called and from where
rg "registerUploadthingTailwindCss" packages/nuxt/src/module.ts

Repository: pingdotgg/uploadthing

Length of output: 164


🏁 Script executed:

# Let's see the full registerUploadthingCss function for comparison
rg -A 15 "function registerUploadthingCss" packages/nuxt/src/module.ts

Repository: pingdotgg/uploadthing

Length of output: 431


🏁 Script executed:

# Get the complete registerUploadthingCss function
cat -n packages/nuxt/src/module.ts | sed -n '185,203p'

Repository: pingdotgg/uploadthing

Length of output: 679


🏁 Script executed:

# Look for any implicit CSS registration mechanism or higher-level registration
rg -B 10 "applyUploadthingStyles()" packages/nuxt/src/module.ts

Repository: pingdotgg/uploadthing

Length of output: 411


🏁 Script executed:

# Let's see if addTemplate return value is used elsewhere with CSS
rg -B 5 -A 5 "const.*=.*addTemplate" packages/nuxt/src/module.ts | head -50

Repository: pingdotgg/uploadthing

Length of output: 393


🏁 Script executed:

# Check if there's any automatic CSS discovery or scanning mechanism
rg "\.css|CSS" packages/nuxt/src/module.ts | grep -v "uploadthing-tw.css" | head -20

Repository: pingdotgg/uploadthing

Length of output: 949


🏁 Script executed:

# Look for any Nuxt hooks or build-time processing that might handle templates
rg "addTemplate|useNuxt" packages/nuxt/src/module.ts | head -30

Repository: pingdotgg/uploadthing

Length of output: 470


🏁 Script executed:

# Let's look at the complete applyUploadthingStyles to see how registerUploadthingTailwindCss is called
cat -n packages/nuxt/src/module.ts | sed -n '145,180p'

Repository: pingdotgg/uploadthing

Length of output: 1486


🏁 Script executed:

# Check how addTemplate is used in the uploadthingHandlerTemplate (which has a return value assigned)
cat -n packages/nuxt/src/module.ts | sed -n '105,135p'

Repository: pingdotgg/uploadthing

Length of output: 1133


🏁 Script executed:

# Let's see if uploadthingHandlerTemplate is used after assignment
rg "uploadthingHandlerTemplate" packages/nuxt/src/module.ts

Repository: pingdotgg/uploadthing

Length of output: 161


🏁 Script executed:

# Let's check Nuxt's addTemplate documentation or comments in the codebase about how templates work
rg -B 3 "addTemplate" packages/nuxt/src/module.ts | grep -A 3 "^--$" | head -30

Repository: pingdotgg/uploadthing

Length of output: 446


🏁 Script executed:

# Let's see if there's a pattern where template.dst is used with nuxt.options.css elsewhere
rg -B 2 -A 2 "template\.dst" packages/nuxt/src/module.ts

Repository: pingdotgg/uploadthing

Length of output: 47


🏁 Script executed:

# Check what the addTemplate return type looks like by examining how uploadthingHandlerTemplate is used
cat -n packages/nuxt/src/module.ts | sed -n '65,100p'

Repository: pingdotgg/uploadthing

Length of output: 1276


Register the generated Tailwind CSS file with Nuxt's CSS pipeline.

registerUploadthingTailwindCss() generates uploadthing-tw.css but never adds it to nuxt.options.css. Unlike registerUploadthingCss() (which calls nuxt.options.css.push('@uploadthing/vue/styles.css')), the Tailwind variant omits this registration, so the generated stylesheet is never loaded by Nuxt. This breaks the Tailwind v4 integration when useTailwindStyles: true is enabled.

Suggested fix
 function registerUploadthingTailwindCss(distPath: string) {
-  addTemplate({
-    write: true,
-    filename: 'uploadthing-tw.css',
-    dst: resolver.resolve('./runtime/uploadthing-tw.css'),
-    getContents: () =>
-      `@import "uploadthing/tw/v4";\n@source "${distPath}";\n`,
-  })
+  const nuxt = useNuxt()
+  const template = addTemplate({
+    filename: 'uploadthing/uploadthing-tw.css',
+    getContents: () =>
+      `@import "uploadthing/tw/v4";\n@source "${distPath}";\n`,
+  })
+  nuxt.options.css.push(template.dst)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function registerUploadthingTailwindCss(distPath: string) {
addTemplate({
write: true,
filename: 'uploadthing-tw.css',
dst: resolver.resolve('./runtime/uploadthing-tw.css'),
getContents: () =>
`@import "uploadthing/tw/v4";\n@source "${distPath}";\n`,
})
function registerUploadthingTailwindCss(distPath: string) {
const nuxt = useNuxt()
const template = addTemplate({
filename: 'uploadthing/uploadthing-tw.css',
getContents: () =>
`@import "uploadthing/tw/v4";\n@source "${distPath}";\n`,
})
nuxt.options.css.push(template.dst)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/nuxt/src/module.ts` around lines 195 - 202,
registerUploadthingTailwindCss currently generates uploadthing-tw.css via
addTemplate but never registers it with Nuxt's CSS pipeline; after the
addTemplate call in registerUploadthingTailwindCss, push the generated CSS into
nuxt.options.css (e.g.
nuxt.options.css.push(resolver.resolve('./runtime/uploadthing-tw.css'))),
mirroring the pattern used in registerUploadthingCss (which calls
nuxt.options.css.push('@uploadthing/vue/styles.css')) so the Tailwind v4
stylesheet is actually loaded by Nuxt.

Comment thread packages/nuxt/src/module.ts Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
packages/nuxt/src/module.ts (2)

175-180: ⚠️ Potential issue | 🟠 Major

Avoid writing generated CSS into the module source runtime path.

On Line 179, forcing dst to resolver.resolve('./runtime/uploadthing-tw.css') can attempt writes under installed package files; use Nuxt build templates instead.

Proposed fix
 function registerUploadthingCss() {
-  addTemplate({
-    write: true,
-    filename: 'uploadthing-tw.css',
-    dst: resolver.resolve('./runtime/uploadthing-tw.css'),
-    getContents: () =>
-      `
-/**
- * Tailwind styles are disabled.
- * UploadThing default CSS is being used instead.
- * This file is intentionally left empty.
- */
-    `,
-  })
-
   const nuxt = useNuxt()
   return nuxt.options.css.push('@uploadthing/vue/styles.css')
 }
#!/bin/bash
# Verify `dst` points to package runtime path in fallback CSS registration
sed -n '174,192p' packages/nuxt/src/module.ts | cat -n
# Expected: `dst: resolver.resolve('./runtime/uploadthing-tw.css')` is present.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/nuxt/src/module.ts` around lines 175 - 180, The addTemplate call in
registerUploadthingCss currently forces dst to
resolver.resolve('./runtime/uploadthing-tw.css'), which may write into installed
package files; remove the dst property (or stop resolving into ./runtime) so
Nuxt's addTemplate controls the output path (use only filename and getContents)
and let Nuxt place the generated CSS into the build/templates output instead of
the package runtime; update the addTemplate invocation in registerUploadthingCss
to omit dst and keep write/filename/getContents.

194-202: ⚠️ Potential issue | 🟠 Major

Tailwind v4 template is generated but never loaded by Nuxt.

On Line 195, the Tailwind stylesheet template is created, but there is no nuxt.options.css.push(...) to register it, so v4 styles won’t be applied.

Proposed fix
 function registerUploadthingTailwindCss(distPath: string) {
-  addTemplate({
-    write: true,
-    filename: 'uploadthing-tw.css',
-    dst: resolver.resolve('./runtime/uploadthing-tw.css'),
+  const nuxt = useNuxt()
+  const template = addTemplate({
+    filename: 'uploadthing/uploadthing-tw.css',
     getContents: () =>
       `@import "uploadthing/tw/v4";\n@source "${distPath}";\n`,
   })
+  nuxt.options.css.push(template.dst)
 }
#!/bin/bash
# Verify current Tailwind registration path is not pushed to Nuxt CSS
sed -n '190,206p' packages/nuxt/src/module.ts | cat -n
rg -n "registerUploadthingTailwindCss|nuxt\\.options\\.css\\.push" packages/nuxt/src/module.ts -A8 -B4
# Expected: no css.push inside registerUploadthingTailwindCss in current code.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/nuxt/src/module.ts` around lines 194 - 202, The
registerUploadthingTailwindCss function creates the uploadthing-tw.css template
but never registers it with Nuxt; update registerUploadthingTailwindCss (the
function that calls addTemplate and generates 'uploadthing-tw.css' via
resolver.resolve('./runtime/uploadthing-tw.css')) to push the generated template
path into nuxt.options.css (e.g., nuxt.options.css.push(<template dst or
filename>)) after addTemplate so the Tailwind v4 stylesheet is actually loaded
by Nuxt.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@packages/nuxt/src/module.ts`:
- Around line 175-180: The addTemplate call in registerUploadthingCss currently
forces dst to resolver.resolve('./runtime/uploadthing-tw.css'), which may write
into installed package files; remove the dst property (or stop resolving into
./runtime) so Nuxt's addTemplate controls the output path (use only filename and
getContents) and let Nuxt place the generated CSS into the build/templates
output instead of the package runtime; update the addTemplate invocation in
registerUploadthingCss to omit dst and keep write/filename/getContents.
- Around line 194-202: The registerUploadthingTailwindCss function creates the
uploadthing-tw.css template but never registers it with Nuxt; update
registerUploadthingTailwindCss (the function that calls addTemplate and
generates 'uploadthing-tw.css' via
resolver.resolve('./runtime/uploadthing-tw.css')) to push the generated template
path into nuxt.options.css (e.g., nuxt.options.css.push(<template dst or
filename>)) after addTemplate so the Tailwind v4 stylesheet is actually loaded
by Nuxt.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fe020624-5bf9-473d-8df3-d2746a70153d

📥 Commits

Reviewing files that changed from the base of the PR and between 724a45d and ea44ba9.

📒 Files selected for processing (3)
  • packages/nuxt/README.md
  • packages/nuxt/package.json
  • packages/nuxt/src/module.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/nuxt/README.md

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/nuxt/src/module.ts`:
- Around line 68-70: The default option fileRouterExport currently set to
'fileRouter' doesn't match the fallback router module's export name
uploadRouter, causing the generated handler (which accesses
RouterModule["fileRouter"]) to fail; fix by making them consistent—either change
the defaults.fileRouterExport value back to 'uploadRouter' or update the
fallback '#ut-router' module to export 'fileRouter' (ensure the symbol names
match the generated handler's lookup RouterModule["fileRouter"]).
- Around line 320-325: The helper emptyStringToUndefined is untyped and uses an
untyped next object which breaks strict TS; update emptyStringToUndefined to
accept a properly typed input (e.g. obj: Record<string, string | undefined> or a
generic <T extends Record<string, string | undefined>>(obj: T)) and type next to
the corresponding output shape (e.g. Record<keyof T, string | undefined> or
Partial<T>) and annotate the function return type to match before passing its
result into createRouteHandler(); ensure all property accesses use the typed
index signature so the compiler accepts next[key] assignments.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7e77a085-93ca-4f6a-8f9a-46d7a961704d

📥 Commits

Reviewing files that changed from the base of the PR and between ea44ba9 and 12dd75c.

📒 Files selected for processing (1)
  • packages/nuxt/src/module.ts

Comment on lines 68 to +70
defaults: {
routerPath: "~/server/uploadthing",
injectStyles: true,
// Need to manually set these for static analysis to work, even if they're set to undefined
token: undefined,
isDev: undefined,
logLevel: undefined,
logFormat: undefined,
ingestUrl: undefined,
callbackUrl: undefined,
fileRouterPath: '@@/server/uploadthing',
fileRouterExport: 'fileRouter',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Align the default router export name with the fallback router module.

fileRouterExport now defaults to fileRouter, but the fallback #ut-router module still exports uploadRouter. With the out-of-the-box config, the generated handler looks up RouterModule["fileRouter"] and throws unless users override the option. Either switch this default back to uploadRouter or rename the fallback export to match.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/nuxt/src/module.ts` around lines 68 - 70, The default option
fileRouterExport currently set to 'fileRouter' doesn't match the fallback router
module's export name uploadRouter, causing the generated handler (which accesses
RouterModule["fileRouter"]) to fail; fix by making them consistent—either change
the defaults.fileRouterExport value back to 'uploadRouter' or update the
fallback '#ut-router' module to export 'fileRouter' (ensure the symbol names
match the generated handler's lookup RouterModule["fileRouter"]).

Comment on lines +320 to +325
const emptyStringToUndefined = (obj) => {
const next = {}
for (const key in obj) {
next[key] = obj[key] === '' ? undefined : obj[key]
}
return next
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's locate and examine the file
fd -t f "module.ts" packages/nuxt/src

Repository: pingdotgg/uploadthing

Length of output: 93


🏁 Script executed:

# Get the file content around lines 320-325
if [ -f "packages/nuxt/src/module.ts" ]; then
  wc -l packages/nuxt/src/module.ts
fi

Repository: pingdotgg/uploadthing

Length of output: 97


🏁 Script executed:

# Read the specific section mentioned in the review
sed -n '310,335p' packages/nuxt/src/module.ts | cat -n

Repository: pingdotgg/uploadthing

Length of output: 1004


🏁 Script executed:

# Check the broader context for how emptyStringToUndefined is used
rg -n "emptyStringToUndefined" packages/nuxt/src/module.ts -A 3 -B 3

Repository: pingdotgg/uploadthing

Length of output: 630


🏁 Script executed:

# Check tsconfig files
find . -name "tsconfig*.json" | head -5 | xargs -I {} sh -c 'echo "=== {} ===" && cat {}'

Repository: pingdotgg/uploadthing

Length of output: 3157


🏁 Script executed:

# Check if RouteHandlerConfig exists in uploadthing/types
rg -n "RouteHandlerConfig" packages/ --type ts --type tsx

Repository: pingdotgg/uploadthing

Length of output: 93


🏁 Script executed:

# Check the context after line 333 to see how config is used
sed -n '333,350p' packages/nuxt/src/module.ts | cat -n

Repository: pingdotgg/uploadthing

Length of output: 652


🏁 Script executed:

# Check if there are h3-specific imports and see the createRouteHandler usage
sed -n '320,356p' packages/nuxt/src/module.ts | cat -n

Repository: pingdotgg/uploadthing

Length of output: 1191


🏁 Script executed:

# Search for RouteHandlerConfig in the uploadthing package
rg "RouteHandlerConfig" packages/ -t ts

Repository: pingdotgg/uploadthing

Length of output: 47


🏁 Script executed:

# Check the uploadthing types exports
find . -path "*/uploadthing/types*" -o -path "*/uploadthing/src/types*" | head -10

Repository: pingdotgg/uploadthing

Length of output: 101


🏁 Script executed:

# Look for RouteHandlerConfig definition more broadly
rg "RouteHandlerConfig" . --type ts -l

Repository: pingdotgg/uploadthing

Length of output: 177


🏁 Script executed:

# Check h3 types in uploadthing
rg "RouteHandlerConfig\|createRouteHandler" packages/core/src/ -A 2 -B 2

Repository: pingdotgg/uploadthing

Length of output: 124


🏁 Script executed:

# Check the uploadthing types file
cat packages/uploadthing/src/types.ts

Repository: pingdotgg/uploadthing

Length of output: 8141


🏁 Script executed:

# Look for h3 related files
find packages -name "*h3*" -type f

Repository: pingdotgg/uploadthing

Length of output: 96


🏁 Script executed:

# Search for createRouteHandler definition
rg "createRouteHandler" packages/ -A 5 -B 2

Repository: pingdotgg/uploadthing

Length of output: 47


🏁 Script executed:

# Check the h3.ts file
cat packages/uploadthing/src/h3.ts

Repository: pingdotgg/uploadthing

Length of output: 1262


🏁 Script executed:

# Also check what's exported from uploadthing/h3
rg "export" packages/uploadthing/src/h3.ts

Repository: pingdotgg/uploadthing

Length of output: 227


Type the generated config sanitizer before this lands.

This template will fail strict TypeScript checks: obj is implicitly any, and next[key] indexes a plain {}. Give the helper an explicit object type and type next accordingly before passing the result into createRouteHandler().

Suggested fix
 import { createRouteHandler } from 'uploadthing/h3'
+import type { RouteHandlerConfig } from 'uploadthing/types'
 import * as RouterModule from '#ut-router'
@@
-const emptyStringToUndefined = (obj) => {
-  const next = {}
-  for (const key in obj) {
-    next[key] = obj[key] === '' ? undefined : obj[key]
+const emptyStringToUndefined = (obj: Record<string, unknown>) => {
+  const next: Record<string, unknown> = {}
+  for (const [key, value] of Object.entries(obj)) {
+    next[key] = value === '' ? undefined : value
   }
   return next
 }
@@
-    const config = emptyStringToUndefined(runtime.uploadthing?.routeHandlerConfig ?? {})
+    const config = emptyStringToUndefined(
+      (runtime.uploadthing?.routeHandlerConfig ?? {}) as Record<string, unknown>,
+    ) as RouteHandlerConfig
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const emptyStringToUndefined = (obj) => {
const next = {}
for (const key in obj) {
next[key] = obj[key] === '' ? undefined : obj[key]
}
return next
const emptyStringToUndefined = (obj: Record<string, unknown>) => {
const next: Record<string, unknown> = {}
for (const [key, value] of Object.entries(obj)) {
next[key] = value === '' ? undefined : value
}
return next
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/nuxt/src/module.ts` around lines 320 - 325, The helper
emptyStringToUndefined is untyped and uses an untyped next object which breaks
strict TS; update emptyStringToUndefined to accept a properly typed input (e.g.
obj: Record<string, string | undefined> or a generic <T extends Record<string,
string | undefined>>(obj: T)) and type next to the corresponding output shape
(e.g. Record<keyof T, string | undefined> or Partial<T>) and annotate the
function return type to match before passing its result into
createRouteHandler(); ensure all property accesses use the typed index signature
so the compiler accepts next[key] assignments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant