Building with Tailwind CSS 

Gadget web apps come pre-configured with Tailwind CSS v4, the latest version of the popular utility-first CSS framework. Tailwind is integrated with the shadcn component system and ready to use out of the box.

Learn more 

For more information about Tailwind CSS v4, visit the official Tailwind CSS documentation.

Migrating from Tailwind v3 to v4 

If you have an existing Gadget app using Tailwind v3, this guide will walk you through upgrading to v4. For comprehensive details on all changes between versions, see the official Tailwind v4 upgrade guide.

Tailwind provides an upgrade CLI tool that can automate parts of this migration. However, Gadget apps have specific configurations that require the manual steps below.

Migration steps 

1. Update dependencies 

First, remove the old Tailwind v3 dependencies that are no longer needed:

terminal
yarn remove tailwindcss-animate postcss autoprefixer
terminal
yarn add @tailwindcss/vite@latest tailwindcss@latest tw-animate-css

2. Upgrade @gadgetinc/react 

Update @gadgetinc/react to version 0.24.0 or higher. This version includes the necessary CSS for shadcn autocomponents in Tailwind v4.

3. Note your custom configuration 

Before removing your config file, review your tailwind.config.js (or tailwind.config.ts) and note any custom configuration you've added, such as:

  • Custom colors
  • Custom spacing values
  • Custom fonts
  • Plugin configurations

You'll migrate these to CSS in a later step.

4. Remove tailwind.config.js 

Delete your tailwind.config.js (or tailwind.config.ts) file. The configuration will now live in your CSS file.

5. Update your CSS file 

Replace your existing web/app.css (or equivalent) with the new v4 format.

Here's a complete example of a v4-compatible app.css:

css
/* web/app.css */ @import "tailwindcss"; @import "@gadgetinc/react/auto/shadcn.css"; @import "tw-animate-css"; @custom-variant dark (&:is(.dark *)); @theme inline { --radius-sm: calc(var(--radius) - 4px); --radius-md: calc(var(--radius) - 2px); --radius-lg: var(--radius); --radius-xl: calc(var(--radius) + 4px); --color-background: var(--background); --color-foreground: var(--foreground); --color-card: var(--card); --color-card-foreground: var(--card-foreground); --color-popover: var(--popover); --color-popover-foreground: var(--popover-foreground); --color-primary: var(--primary); --color-primary-foreground: var(--primary-foreground); --color-secondary: var(--secondary); --color-secondary-foreground: var(--secondary-foreground); --color-muted: var(--muted); --color-muted-foreground: var(--muted-foreground); --color-accent: var(--accent); --color-accent-foreground: var(--accent-foreground); --color-destructive: var(--destructive); --color-border: var(--border); --color-input: var(--input); --color-ring: var(--ring); --color-chart-1: var(--chart-1); --color-chart-2: var(--chart-2); --color-chart-3: var(--chart-3); --color-chart-4: var(--chart-4); --color-chart-5: var(--chart-5); --color-sidebar: var(--sidebar); --color-sidebar-foreground: var(--sidebar-foreground); --color-sidebar-primary: var(--sidebar-primary); --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); --color-sidebar-accent: var(--sidebar-accent); --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); --color-sidebar-border: var(--sidebar-border); --color-sidebar-ring: var(--sidebar-ring); } :root { --radius: 0.625rem; --background: oklch(1 0 0); --foreground: oklch(0.141 0.005 285.823); --card: oklch(1 0 0); --card-foreground: oklch(0.141 0.005 285.823); --popover: oklch(1 0 0); --popover-foreground: oklch(0.141 0.005 285.823); --primary: oklch(0.21 0.006 285.885); --primary-foreground: oklch(0.985 0 0); --secondary: oklch(0.967 0.001 286.375); --secondary-foreground: oklch(0.21 0.006 285.885); --muted: oklch(0.967 0.001 286.375); --muted-foreground: oklch(0.552 0.016 285.938); --accent: oklch(0.967 0.001 286.375); --accent-foreground: oklch(0.21 0.006 285.885); --destructive: oklch(0.577 0.245 27.325); --border: oklch(0.92 0.004 286.32); --input: oklch(0.92 0.004 286.32); --ring: oklch(0.705 0.015 286.067); --chart-1: oklch(0.646 0.222 41.116); --chart-2: oklch(0.6 0.118 184.704); --chart-3: oklch(0.398 0.07 227.392); --chart-4: oklch(0.828 0.189 84.429); --chart-5: oklch(0.769 0.188 70.08); --sidebar: oklch(0.985 0 0); --sidebar-foreground: oklch(0.141 0.005 285.823); --sidebar-primary: oklch(0.21 0.006 285.885); --sidebar-primary-foreground: oklch(0.985 0 0); --sidebar-accent: oklch(0.967 0.001 286.375); --sidebar-accent-foreground: oklch(0.21 0.006 285.885); --sidebar-border: oklch(0.92 0.004 286.32); --sidebar-ring: oklch(0.705 0.015 286.067); } .dark { --background: oklch(0.141 0.005 285.823); --foreground: oklch(0.985 0 0); --card: oklch(0.21 0.006 285.885); --card-foreground: oklch(0.985 0 0); --popover: oklch(0.21 0.006 285.885); --popover-foreground: oklch(0.985 0 0); --primary: oklch(0.985 0 0); --primary-foreground: oklch(0.21 0.006 285.885); --secondary: oklch(0.274 0.006 286.033); --secondary-foreground: oklch(0.985 0 0); --muted: oklch(0.274 0.006 286.033); --muted-foreground: oklch(0.705 0.015 286.067); --accent: oklch(0.274 0.006 286.033); --accent-foreground: oklch(0.985 0 0); --destructive: oklch(0.704 0.191 22.216); --border: oklch(0.274 0.006 286.033); --input: oklch(0.274 0.006 286.033); --ring: oklch(0.442 0.017 285.786); --chart-1: oklch(0.488 0.243 264.376); --chart-2: oklch(0.696 0.17 162.48); --chart-3: oklch(0.769 0.188 70.08); --chart-4: oklch(0.627 0.265 303.9); --chart-5: oklch(0.645 0.246 16.439); --sidebar: oklch(0.21 0.006 285.885); --sidebar-foreground: oklch(0.985 0 0); --sidebar-primary: oklch(0.488 0.243 264.376); --sidebar-primary-foreground: oklch(0.985 0 0); --sidebar-accent: oklch(0.274 0.006 286.033); --sidebar-accent-foreground: oklch(0.985 0 0); --sidebar-border: oklch(0.274 0.006 286.033); --sidebar-ring: oklch(0.442 0.017 285.786); } @layer base { * { @apply border-border outline-ring/50; } body { @apply bg-background text-foreground font-sans antialiased; } }

The @import "@gadgetinc/react/auto/shadcn.css" directive is required if you are using Gadget's shadcn autocomponents. Make sure you have @gadgetinc/react version 0.24.0 or higher.

6. Migrate custom theme configuration 

If you had custom theme configuration in your old tailwind.config.js, migrate it to the @theme directive in your CSS.

Old (tailwind.config.js):

JavaScript
// tailwind.config.js module.exports = { theme: { extend: { colors: { brand: { 50: "#eff6ff", 100: "#dbeafe", }, }, spacing: { 128: "32rem", }, }, }, };
// tailwind.config.js module.exports = { theme: { extend: { colors: { brand: { 50: "#eff6ff", 100: "#dbeafe", }, }, spacing: { 128: "32rem", }, }, }, };

New (app.css):

css
/* Add to your @theme block in web/app.css */ @theme { --color-brand-50: #eff6ff; --color-brand-100: #dbeafe; --spacing-128: 32rem; }

7. Remove postcss config and use the Tailwind Vite plugin 

Delete your postcss.config.js file if you have one.

Update your vite.config.ts to use the Tailwind Vite plugin:

JavaScript
// vite.config.ts import tailwindcss from "@tailwindcss/vite"; export default defineConfig({ plugins: [tailwindcss()], });
// vite.config.ts import tailwindcss from "@tailwindcss/vite"; export default defineConfig({ plugins: [tailwindcss()], });

8. Remove reset.min.css 

In your web/root.tsx file, remove the reset.min.css stylesheet link if present:

React
// Remove this from your links function in web/root.tsx export const links = () => [{ rel: "stylesheet", href: "https://assets.gadget.dev/assets/reset.min.css" }];
// Remove this from your links function in web/root.tsx export const links = () => [{ rel: "stylesheet", href: "https://assets.gadget.dev/assets/reset.min.css" }];

Tailwind v4 includes its own preflight styles, so this external reset is no longer needed.

9. Update ChatGPT widget CSS (if applicable) 

If your app has a ChatGPT connection with a custom widget, update the import syntax in web/chatgpt/chatgpt.css:

Old:

css
@import url("../app.css");

New:

css
@import "../app.css";

The url() function is not compatible with Tailwind v4's CSS imports.

Breaking changes 

For a complete list of breaking changes and deprecated features, see the official Tailwind v4 upgrade guide.

Was this page helpful?