Skip to content
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/friendly-poets-scream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@uploadthing/react": minor
---

feat: add tanstack start ssr integration
5 changes: 5 additions & 0 deletions .changeset/pink-rabbits-shave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"uploadthing": minor
---

feat: add `react-router` adapter
1 change: 0 additions & 1 deletion .nvmrc

This file was deleted.

24 changes: 12 additions & 12 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,19 @@
"browserslist": "defaults, not ie <= 11",
"dependencies": {
"@algolia/autocomplete-core": "^1.18.0",
"@headlessui/react": "^2.2.0",
"@headlessui/tailwindcss": "^0.2.1",
"@headlessui/react": "^2.2.9",
"@headlessui/tailwindcss": "^0.2.2",
"@heroicons/react": "^2.2.0",
"@mdx-js/loader": "^3.1.0",
"@mdx-js/react": "^3.1.0",
"@next/mdx": "15.5.8",
"@next/mdx": "15.5.9",
"@scalar/api-reference-react": "^0.4.25",
"@shikijs/transformers": "^1.29.2",
"@sindresorhus/slugify": "^2.1.1",
"@tailwindcss/typography": "^0.5.15",
"@tailwindcss/typography": "^0.5.19",
"@types/mdast": "^4.0.4",
"@types/mdx": "^2.0.13",
"@types/node": "^22.10.0",
"@types/node": "^25.0.2",
"@types/react": "19.2.7",
"@types/react-dom": "19.2.3",
"@types/react-highlight-words": "^0.20.0",
Expand All @@ -36,14 +36,14 @@
"mdast-util-to-string": "^4.0.0",
"mdx-annotations": "^0.1.4",
"motion": "^12.0.6",
"next": "15.5.8",
"next": "15.5.9",
"next-sitemap": "^4.2.3",
"next-themes": "^0.4.6",
"next-view-transitions": "^0.3.4",
"next-view-transitions": "^0.3.5",
"npm-to-yarn": "^3.0.0",
"react": "19.2.2",
"react-dom": "19.2.2",
"react-highlight-words": "^0.20.0",
"react": "19.1.4",
"react-dom": "19.1.4",
"react-highlight-words": "^0.21.0",
"recma-import-images": "^0.0.3",
"remark": "^15.0.1",
"remark-gfm": "^4.0.0",
Expand All @@ -52,8 +52,8 @@
"sharp": "0.33.5",
"shiki": "^1.29.2",
"simple-functional-loader": "^1.2.1",
"tailwindcss": "^3.4.16",
"typescript": "5.8.3",
"tailwindcss": "^4.1.18",
"typescript": "5.9.3",
"unified": "^11.0.5",
"unist-util-filter": "^5.0.1",
"unist-util-visit": "^5.0.0",
Expand Down
10 changes: 5 additions & 5 deletions examples/backend-adapters/client-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@
},
"dependencies": {
"@uploadthing/react": "7.3.3",
"react": "19.2.2",
"react-dom": "19.2.2",
"react": "19.1.4",
"react-dom": "19.1.4",
"uploadthing": "7.7.4"
},
"devDependencies": {
"@types/react": "19.2.7",
"@types/react-dom": "19.2.3",
"@vitejs/plugin-react-swc": "^3.7.2",
"typescript": "5.8.3",
"vite": "^6.3.4"
"@vitejs/plugin-react-swc": "^4.2.2",
"typescript": "5.9.3",
"vite": "7.2.7"
}
}
4 changes: 2 additions & 2 deletions examples/backend-adapters/client-vanilla/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"uploadthing": "7.7.4"
},
"devDependencies": {
"typescript": "5.8.3",
"vite": "^6.3.4"
"typescript": "5.9.3",
"vite": "7.2.7"
}
}
6 changes: 3 additions & 3 deletions examples/backend-adapters/client-vue/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
"vue": "^3.4.21"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.1",
"typescript": "5.8.3",
"vite": "^6.3.4",
"@vitejs/plugin-vue": "^6.0.3",
"typescript": "5.9.3",
"vite": "7.2.7",
"vue-tsc": "^2.0.6"
}
}
2 changes: 1 addition & 1 deletion examples/backend-adapters/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"@playwright/test": "1.52.0",
"@uploadthing/react": "7.3.3",
"concurrently": "^9.1.2",
"typescript": "5.8.3",
"typescript": "5.9.3",
"uploadthing": "7.7.4"
}
}
16 changes: 8 additions & 8 deletions examples/backend-adapters/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@
"dev:effect": "NODE_ENV=development PORT=3003 tsx watch src/effect-platform.ts"
},
"dependencies": {
"@effect/platform": "0.90.3",
"@effect/platform-node": "0.96.0",
"@effect/platform": "0.93.8",
"@effect/platform-node": "0.103.0",
"@elysiajs/cors": "^1.2.0",
"@fastify/cors": "^10.0.1",
"@hono/node-server": "^1.13.7",
"@sinclair/typebox": "^0.34.13",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"effect": "3.17.7",
"effect": "3.19.12",
"elysia": "^1.2.9",
"express": "^5.0.1",
"fastify": "^5.2.0",
Expand All @@ -31,12 +31,12 @@
"uploadthing": "7.7.4"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20241230.0",
"@types/bun": "^1.2.5",
"@types/cors": "^2.8.17",
"@types/express": "^5.0.0",
"@cloudflare/workers-types": "^4.20251213.0",
"@types/bun": "^1.3.4",
"@types/cors": "^2.8.19",
"@types/express": "^5.0.6",
"tsx": "^4.19.2",
"typescript": "5.8.3",
"typescript": "5.9.3",
"wrangler": "^3.99.0"
}
}
1 change: 0 additions & 1 deletion examples/minimal-appdir/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ const analyze = require("@next/bundle-analyzer")({

/** @type {import('next').NextConfig} */
const nextConfig = {
eslint: { ignoreDuringBuilds: true },
typescript: { ignoreBuildErrors: true },
};

Expand Down
12 changes: 6 additions & 6 deletions examples/minimal-appdir/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@
},
"dependencies": {
"@uploadthing/react": "7.3.3",
"next": "15.5.8",
"react": "19.2.2",
"react-dom": "19.2.2",
"next": "15.5.9",
"react": "19.1.4",
"react-dom": "19.1.4",
"uploadthing": "7.7.4"
},
"devDependencies": {
"@next/bundle-analyzer": "15.1.3",
"@types/node": "^22.10.0",
"@next/bundle-analyzer": "15.5.9",
"@types/node": "^25.0.2",
"@types/react": "19.2.7",
"@types/react-dom": "19.2.3",
"typescript": "5.8.3"
"typescript": "5.9.3"
}
}
8 changes: 4 additions & 4 deletions examples/minimal-astro-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@
"@astrojs/react": "^4.1.2",
"@uploadthing/react": "7.3.3",
"astro": "^5.1.2",
"react": "19.2.2",
"react-dom": "19.2.2",
"react": "19.1.4",
"react-dom": "19.1.4",
"uploadthing": "7.7.4"
},
"devDependencies": {
"@astrojs/check": "0.9.4",
"@astrojs/check": "0.9.6",
"@types/react": "19.2.7",
"@types/react-dom": "19.2.3",
"typescript": "5.8.3"
"typescript": "5.9.3"
}
}
33 changes: 19 additions & 14 deletions examples/minimal-expo/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,41 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
...config,
name: "Minimal Expo x UploadThing",
slug: "minimal-expo",
version: "1.0.0",
version: "0.1.0",
orientation: "portrait",
icon: "./assets/icon.png",
scheme: "myapp",
userInterfaceStyle: "dark",
splash: {
backgroundColor: "#ffffff",
userInterfaceStyle: "automatic",
updates: {
fallbackToCacheTimeout: 0,
},
newArchEnabled: true,
assetBundlePatterns: ["**/*"],
ios: {
bundleIdentifier: "your.bundle.identifier",
supportsTablet: true,
icon: {
light: "./assets/icon.png",
dark: "./assets/icon.png",
},
},
android: {
package: "your.bundle.identifier",
adaptiveIcon: {
foregroundImage: "./assets/icon.png",
backgroundColor: "#ffffff",
},
},
web: {
bundler: "metro",
output: "server",
},
plugins: ["expo-router"],
experiments: {
typedRoutes: true,
edgeToEdgeEnabled: true,
},
// extra: {
// eas: {
// projectId: "",
// projectId: "your-eas-project-id",
// },
// },
experiments: {
tsconfigPaths: true,
typedRoutes: true,
reactCanary: true,
reactCompiler: true,
},
Comment on lines +37 to +42
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check package.json for versions
echo "=== Expo and React versions ==="
cat examples/minimal-expo/package.json | grep -E '"expo"|"react"|"react-native"' | head -20

echo ""
echo "=== Checking for babel config ==="
fd -t f 'babel' examples/minimal-expo

echo ""
echo "=== Full package.json for context ==="
cat examples/minimal-expo/package.json

Repository: pingdotgg/uploadthing

Length of output: 1978


🏁 Script executed:

cat examples/minimal-expo/babel.config.js

Repository: pingdotgg/uploadthing

Length of output: 237


Add React Compiler Babel plugin to support reactCompiler experiment.

The reactCompiler flag in experiments requires the babel-plugin-react-compiler Babel plugin to actually compile. The current babel.config.js only includes react-native-reanimated/plugin and is missing the React Compiler plugin configuration. Without it, the reactCompiler flag will have no effect. Add the plugin to babel.config.js:

plugins: [
  "babel-plugin-react-compiler",
  "react-native-reanimated/plugin"
]

The other flags (tsconfigPaths, typedRoutes, reactCanary) are compatible with Expo 54 and React 19.1.4.

🤖 Prompt for AI Agents
In examples/minimal-expo/app.config.ts around lines 37 to 42, the experiments
enable reactCompiler but the project’s Babel config doesn’t include the required
babel-plugin-react-compiler, so the reactCompiler flag is ineffective; update
babel.config.js to add "babel-plugin-react-compiler" to the plugins array
(ensuring it is included before/react-native-reanimated/plugin if order
matters), then verify Babel config loads and rebuilds to confirm the React
Compiler is applied.

plugins: ["expo-router"],
});
18 changes: 9 additions & 9 deletions examples/minimal-expo/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { QueryClientProvider } from "@tanstack/react-query";
import { Image } from "expo-image";
import { Stack } from "expo-router";
import { StatusBar } from "expo-status-bar";
import { cssInterop } from "nativewind";
// import { cssInterop } from "nativewind";
import {
GestureHandlerRootView,
RectButton,
Expand Down Expand Up @@ -40,14 +40,14 @@ export default function RootLayout() {
* Add CSS Styling to some 3rd party components
* @see https://www.nativewind.dev/v4/guides/third-party-components#handling-components-with-style-attribute-props
*/
cssInterop(BottomSheetView, { className: "style" });
cssInterop(BottomSheetModal, {
className: "style",
bgClassName: "backgroundStyle",
});
cssInterop(FeatherIcon, { className: "style" });
cssInterop(Image, { className: "style" });
cssInterop(RectButton, { className: "style" });
// cssInterop(BottomSheetView, { className: "style" });
// cssInterop(BottomSheetModal, {
// className: "style",
// bgClassName: "backgroundStyle",
// });
// cssInterop(FeatherIcon, { className: "style" });
// cssInterop(Image, { className: "style" });
// cssInterop(RectButton, { className: "style" });

/**
* Add className type to props of some 3rd party components that don't have it
Expand Down
2 changes: 1 addition & 1 deletion examples/minimal-expo/app/f/[key].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default function FileScreen() {

return (
<>
<Stack.Screen options={{ title: name, headerBackTitleVisible: false }} />
<Stack.Screen options={{ title: name, headerBackVisible: false }} />
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's read the file to understand the context around line 17
cat -n examples/minimal-expo/app/f/[key].tsx

Repository: pingdotgg/uploadthing

Length of output: 186


🌐 Web query:

expo-router 6.0.19 headerBackVisible Stack.Screen options support

💡 Result:

Yes — expo-router (Stack) supports headerBackVisible on Stack.Screen options (it uses React Navigation's native-stack under the hood). It will hide/show the header back button and has no effect on the first screen in the stack.

Example:
import { Stack } from 'expo-router';

export default function Layout() {
return (

<Stack.Screen name="details" options={{ headerBackVisible: false }} />

);
}

Sources: Expo Router docs (Stack header options). [1] React Navigation native-stack docs (headerBackVisible). [2]

References
[1] Expo Router — Stack (header options).
[2] React Navigation — Native Stack Navigator (headerBackVisible).


Ensure users can still navigate back after hiding the header back button

headerBackVisible: false hides the back button in the header. This option is supported in expo-router, but make sure users still have a way to go back—either through gesture navigation, hardware back button, or a custom headerLeft action. (Note: this option has no effect if this is the first screen in the stack.)

🤖 Prompt for AI Agents
In examples/minimal-expo/app/f/[key].tsx around line 17, the Stack.Screen sets
headerBackVisible: false which removes the header back button; update the screen
options so users still can navigate back by either (1) only setting
headerBackVisible: false when the screen is not the first in the stack, (2)
enable gesture navigation (gestureEnabled: true) so swipes work on iOS/Android,
and/or (3) provide a custom headerLeft that calls navigation.goBack() or
router.back(); additionally ensure Android hardware back is respected (handle
BackHandler or rely on navigation container) so there is always at least one
working back action.

<View className="flex h-full items-center justify-center">
{!isImage(name) ? (
<View className="flex flex-col items-center gap-4">
Expand Down
8 changes: 5 additions & 3 deletions examples/minimal-expo/app/styles.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "tailwindcss/theme.css" layer(theme);
@import "tailwindcss/preflight.css" layer(base);
@import "tailwindcss/utilities.css";

@import "nativewind/theme";
Comment on lines +1 to +5
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

NativeWind Tailwind CSS v4 configuration guide

💡 Result:

Quick config guide — NativeWind + Tailwind CSS v4

  1. Install dependencies
  • npm install nativewind tailwindcss@^4 react-native-reanimated react-native-safe-area-context. Adjust versions for Expo/CLI as needed. [1][2]
  1. Create Tailwind config
  • npx tailwindcss init
  • Ensure tailwind.config.js has a content glob covering your RN/Expo files:
    module.exports = { content: ["./App.{js,jsx,ts,tsx}", "./**/*.{js,jsx,ts,tsx}"], theme:{extend:{}}, plugins:[] } . [1]
  1. Replace Tailwind v3 directives with v4 imports in your global CSS
  • Example global.css (Tailwind v4):
    @import "tailwindcss/theme.css" layer(theme);
    @import "tailwindcss/preflight.css" layer(base);
    @import "tailwindcss/utilities.css";
    @import "nativewind/theme";
    This is required for Tailwind v4-style build output. [3]
  1. NativeWind build/metro integration
  • Use NativeWind’s configuration/wrappers so Metro or Tailwind CLI generates native styles. See NativeWind’s configuration options (input, configFile, outputDir, inlineNativeRem, etc.) and the withNativeWind helper where applicable. If your project still uses the Babel approach, follow NativeWind docs for the recommended runtime/transform for v4. [1][2]
  1. Optional: rem handling & migration notes
  • v4 supports rem inlining (inlineNativeRem option). If migrating from older NativeWind versions follow the official migration notes (remove TailwindProvider, ensure tailwind.config.js required, update CSS imports). [2][3]
  1. Verify
  • Run the Tailwind CLI or start your bundler/dev server. Check that classes applied to native components produce styles and that hot-reload responds to tailwind.config changes. Consult NativeWind docs for platform-specific steps (Expo, framework-less, Next.js). [1]

References

  • NativeWind configuration & docs. [1]
  • NativeWind v4 announcement (architecture & rem support). [2]
  • NativeWind v5 migration notes demonstrating Tailwind v4 import patterns. [3]

If you want, I can generate a ready-to-run example repo config (package.json, tailwind.config.js, global.css, and metro/babel snippets).


🌐 Web query:

NativeWind @import layer utilities CSS v4

💡 Result:

Short answer — for Tailwind CSS v4 with NativeWind you must switch from @tailwind directives to CSS @import layer imports and include NativeWind's theme. Example global CSS:

Notes:

  • v4 uses @import + layer(...) / new @utility/@utility-name rules instead of @layer utilities; @apply semantics changed in some cases (custom utilities/components may need defining with @Utility or moved to components). See migration guidance. [1][2][3]

Sources:
[1] NativeWind migration guide (v4 → v5 example showing v4 imports).
[2] Tailwind v4 docs / beta notes on using @import and @Utility.
[3] Community discussion (Stack Overflow / threads) about @apply differences and @layer@Utility changes.


🏁 Script executed:

# Find and examine package.json in minimal-expo to check versions
find . -path "*/minimal-expo/package.json" -type f | head -1 | xargs cat 2>/dev/null

Repository: pingdotgg/uploadthing

Length of output: 1756


🏁 Script executed:

# Search for other CSS files with Tailwind imports to compare patterns
rg -t css "@import.*tailwindcss" --max-count=20

Repository: pingdotgg/uploadthing

Length of output: 1143


Add layer(utilities) to line 3 for consistency with NativeWind integration pattern.

The import pattern is correct for NativeWind + Tailwind v4 integration (documented in NativeWind's official setup guide). However, line 3 should include the explicit layer declaration to match the pattern used elsewhere in the codebase (packages/vue, packages/svelte, packages/react, packages/solid all use layer(utilities)):

@import "tailwindcss/utilities.css" layer(utilities);
🤖 Prompt for AI Agents
In examples/minimal-expo/app/styles.css around lines 1 to 5, the third import of
tailwind utilities is missing the explicit layer declaration; update the line to
import "tailwindcss/utilities.css" with layer(utilities) so it reads the same
pattern as other packages (i.e., add layer(utilities) to that import) to ensure
consistency with NativeWind/Tailwind v4 integration.

4 changes: 0 additions & 4 deletions examples/minimal-expo/babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@
module.exports = function (api) {
api.cache(true);
return {
presets: [
["babel-preset-expo", { jsxImportSource: "nativewind" }],
"nativewind/babel",
],
plugins: ["react-native-reanimated/plugin"],
};
};
1 change: 0 additions & 1 deletion examples/minimal-expo/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
import "@bacons/text-decoder/install";
import "expo-router/entry";
54 changes: 4 additions & 50 deletions examples/minimal-expo/metro.config.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,7 @@
const { getDefaultConfig } = require("expo/metro-config");
// const { FileStore } = require("@expo/metro-config/file-store");
const { withNativeWind } = require("nativewind/metro");
const path = require("path");
const { withNativewind } = require("nativewind/metro");

module.exports = withTurborepoManagedCache(
withMonorepoPaths(
withNativeWind(getDefaultConfig(__dirname), {
input: "./app/styles.css",
configPath: "./tailwind.config.ts",
}),
),
);
const config = getDefaultConfig(__dirname);

/**
* Add the monorepo paths to the Metro config.
* This allows Metro to resolve modules from the monorepo.
*
* @see https://docs.expo.dev/guides/monorepos/#modify-the-metro-config
* @param {import('expo/metro-config').MetroConfig} config
* @returns {import('expo/metro-config').MetroConfig}
*/
function withMonorepoPaths(config) {
const projectRoot = __dirname;
const workspaceRoot = path.resolve(projectRoot, "../..");

// #1 - Watch all files in the monorepo
config.watchFolders = [workspaceRoot];

// #2 - Resolve modules within the project's `node_modules` first, then all monorepo modules
config.resolver.nodeModulesPaths = [
path.resolve(projectRoot, "node_modules"),
path.resolve(workspaceRoot, "node_modules"),
];

return config;
}

/**
* Move the Metro cache to the `node_modules/.cache/metro` folder.
* This repository configured Turborepo to use this cache location as well.
* If you have any environment variables, you can configure Turborepo to invalidate it when needed.
*
* @see https://turbo.build/repo/docs/reference/configuration#env
* @param {import('expo/metro-config').MetroConfig} config
* @returns {import('expo/metro-config').MetroConfig}
*/
function withTurborepoManagedCache(config) {
// config.cacheStores = [
// new FileStore({ root: path.join(__dirname, "node_modules/.cache/metro") }),
// ];
return config;
}
/** @type {import('expo/metro-config').MetroConfig} */
module.exports = withNativewind(config);
4 changes: 3 additions & 1 deletion examples/minimal-expo/nativewind-env.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
/// <reference types="nativewind/types" />
/// <reference types="react-native-css/types" />

// NOTE: This file should not be edited and should be committed with your source code. It is generated by react-native-css. If you need to move or disable this file, please see the documentation.
Loading
Loading