Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tailwind v4 and React 19 #6585

Closed
shadcn opened this issue Feb 6, 2025 · 108 comments
Closed

Tailwind v4 and React 19 #6585

shadcn opened this issue Feb 6, 2025 · 108 comments

Comments

@shadcn
Copy link
Collaborator

shadcn commented Feb 6, 2025

It’s here! Tailwind v4 and React 19. Ready for you to try out.

TLDR

If you're starting a new project with Tailwind v4 and React 19, use the canary version of the command-line:

npx shadcn@canary init

What's New

  • The CLI (canary) can now initialize projects with Tailwind v4.
  • Full support for the new @theme directive and @theme inline option.
  • All components are updated for Tailwind v4 and React 19.
  • We’ve removed the forwardRefs and adjusted the types.
  • Every primitive now has a data-slot attribute for styling.
  • We've fixed and cleaned up the style of the components.
  • We're deprecating the toast component in favor of sonner.
  • Buttons now use the default cursor
  • We're deprecating the default style. New projects will use new-york.

Note: this is non-breaking. Your existing apps with Tailwind v3 and React 18 will still work. When you add new components, they'll still be in v3 and React 18. Only new projects start with v4 and React 19.

What's Coming Next

The following is still being worked on. I'll post updates here.

  • Migrating colors to OKLCH.
  • Fix and improve animations.

See it Live

I put together a demo with all the updated components here: https://v4.shadcn.com

Take a look and test the components. If you find any bugs, leave a comment below.

Try It Out

You can test Tailwind v4 + React 19 today using the canary release of the CLI.

pnpm dlx shadcn@canary init

I'm still working on the docs, but here's a quick guide to testing new projects:

Next.js

  1. Start a new project with Tailwind v4 and React 19:
pnpm create next-app@canary --tailwind --eslint --typescript --app --no-src-dir
  1. Init shadcn/ui. This will create your components.json and set up your CSS variables:
pnpm dlx shadcn@canary init
  1. You should now be able to add components:
pnpm dlx shadcn@canary add button

Vite

  1. Create a new project with React 19:
pnpm create vite --template=react-ts
  1. Follow the official guide to add Tailwind CSS: https://tailwindcss.com/docs/installation/using-vite

  2. Add path aliases to tsconfig.json:

{
  "files": [],
  "references": [
    { "path": "./tsconfig.app.json" },
    { "path": "./tsconfig.node.json" }
  ],
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}
  1. Add path aliases to tsconfig.app.json:
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": [
        "./src/*"
      ]
    }
  }
}
  1. Install @types/node:
pnpm add -D @types/node
  1. Add resolve alias config to vite.config.ts:
import path from "path";
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
import tailwindcss from "@tailwindcss/vite";

export default defineConfig({
  plugins: [react(), tailwindcss()],
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
});
  1. Init shadcn/ui. This will create your components.json and set up your CSS variables:
pnpm dlx shadcn@canary init
  1. You should now be able to add components:
pnpm dlx shadcn@canary add button

(Note: If you need help with other frameworks, drop a comment below. I'll update the guide)

Upgrade Your Project

One of the major advantages of using shadcn/ui is that the code you end up with is exactly what you'd write yourself. There are no hidden abstractions.

This means when a dependency has a new release, you can just follow the official upgrade paths.

Here's how to upgrade your existing projects (full docs are on the way):

1. Follow the Tailwind v4 Upgrade Guide

  • Upgrade to Tailwind v4 by following the official upgrade guide: https://tailwindcss.com/docs/upgrade-guide
  • Use the @tailwindcss/upgrade@next codemod to remove deprecated utility classes and update tailwind config.

2. Update your CSS variables

The codemod will migrate your CSS variables as references under the @theme directive.

@layer base {
  :root {
    --background: 0 0% 100%;
    --foreground: 0 0% 3.9%;
  }
}

@theme {
  --color-background: hsl(var(--background));
  --color-foreground: hsl(var(--foreground));
}

This works. But to make it easier to work with colors and other variables, we'll need to move the hsl wrappers and use @theme inline.

Here's how you do it:

  1. Move :root and .dark out of the @layer base.
  2. Wrap the color values in hsl()
  3. Add the inline option to @theme i.e @theme inline {
  4. Remove the hsl() wrappers from @theme
:root { // <-- Moved outside of @layer
  --background: hsl(0 0% 100%); // <-- Wrap in hsl
  --foreground: hsl(0 0% 3.9%);
}

dark { // <-- Moved outside of @layer
  --background: hsl(0 0% 3.9%);
  --foreground: hsl(0 0% 98%);
}

@theme inline { // <-- Add inline option
  --color-background: var(--background); // <-- Remove hsl
  --color-foreground: var(--foreground);
}

This change makes it much simpler to access your theme variables in both utility classes and outside of CSS for eg. using color values in JavaScript.

3. Update colors for charts

Now that the theme colors come with hsl(), you can remove the wrapper in your chartConfig:

const chartConfig = {
  desktop: {
    label: "Desktop",
-    color: "hsl(var(--chart-1))",
+    color: "var(--chart-1)",
  },
  mobile: {
    label: "Mobile",
-   color: "hsl(var(--chart-2))",
+   color: "var(--chart-2)",
  },
} satisfies ChartConfig

Use new size-* utility

The new size-* utility (added in Tailwind v3.4), is now fully supported by tailwind-merge. You can replace w-* h-* with the new size-* utility:

- w-4 h-4
+ size-4

Update your dependencies

pnpm up "@radix-ui/*" cmdk lucide-react recharts tailwind-merge clsx --latest

Remove forwardRef

You can use the preset-19 codemod to migrate your forwardRef to props or manually update the primitives.

For the codemod, see https://react.dev/blog/2024/04/25/react-19-upgrade-guide#typescript-changes.

If you want to do it manually, here's how to do it step by step:

  1. Replace React.forwardRef<...> with React.ComponentProps<...>
  2. Remove ref={ref} from the component.
  3. Add a data-slot attribute. This will come in handy for styling with Tailwind.
  4. You can optionally convert to a named function and remove the displayName.

Before

const AccordionItem = React.forwardRef<
  React.ElementRef<typeof AccordionPrimitive.Item>,
  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
>(({ className, ...props }, ref) => (
  <AccordionPrimitive.Item
    ref={ref}
    className={cn("border-b last:border-b-0", className)}
    {...props}
  />
))
AccordionItem.displayName = "AccordionItem"

After

function AccordionItem({
  className,
  ...props
}: React.ComponentProps<typeof AccordionPrimitive.Item>) {
  return (
    <AccordionPrimitive.Item
      data-slot="accordion-item"
      className={cn("border-b last:border-b-0", className)}
      {...props}
    />
  )
}

Share Your Feedback

We’d love for you to test these updates and share your feedback! If you run into anything weird or have suggestions, add a comment below.

@dericdinudaniel
Copy link

Goat 🐐

@kurumeii
Copy link

kurumeii commented Feb 6, 2025

I actually don't like the sonner toast but okay 👍

@yasseralsaidi
Copy link

Great, thanks!

@robert-sarosi
Copy link

thanks a lot @shadcn ! tested with a fresh nextjs and vite setup and it worked correctly.

@AltairInglorious
Copy link

Thanks

@uditalias
Copy link

Tested with react router v7 and it works perfectly!

Thank you @shadcn 🙏

@sachasayan
Copy link

Woohoo! Thanks @shadcn!

@marenostrum1981
Copy link

Needed to import "path" in step 5: pnpm install --save-dev @types/node

@goradels
Copy link

goradels commented Feb 6, 2025

Any advice for remix+vite?

✔ Preflight checks.
✔ Verifying framework. Found Remix.
✖ Validating Tailwind CSS.
✔ Validating import alias.

No Tailwind CSS configuration found at /Users/johndoe/Projects/shopify-demo/test-storefront.
It is likely you do not have Tailwind CSS installed or have an invalid configuration.
Install Tailwind CSS then try again.
Visit https://tailwindcss.com/docs/guides/remix to get started.```

@YasinKuralay
Copy link

YasinKuralay commented Feb 6, 2025

Thanks for the update! It works with a clean Vite + React Router V7 setup, however I don't get asked whether to use new-york or default styles in the CLI, and just get new-york @shadcn

Also, changing the "style": "new-york" in components.json to "style": "default" and then adding a new component like npx shadcn@canary add button seems to still load new-york styles. I might be doing sth wrong though, it's my first time using shadcn. Maybe @uditalias can confirm as using same setup?

@ryandotelliott
Copy link

ryandotelliott commented Feb 7, 2025

Exciting stuff! Just want to note that setting up a fresh project with bunx create-next-app@canary --tailwind --eslint --typescript --app --no-src-dir and then initializing with bunx --bun shadcn@canary init does give warnings for peer dependency with React 19.

Image

Thanks a ton for the update 🙏

@daFoggo
Copy link

daFoggo commented Feb 7, 2025

Am I able to upgrade to stable release if i start with these canary version?

@subproject22
Copy link

Remove forwardRef

Does this means shadcn will not work with React 18 and older versions?

@sheldonj
Copy link

Is there a shadcn tailwind v4 monorepo example somewhere?
Right now the instructions for TW4 seem to be for a standalone app, and the mono repo doc instructions are still creating a v3 mono repo setup.

just wondering if there is a documented way to achieve this, or an example repo somewhere?

@Gzbox
Copy link

Gzbox commented Feb 17, 2025

Thanks for the great work, is there an estimated time for the official release? I really want to use the latest version in the production environment of the latest project, but there is no official release, some concerns. @shadcn

Jehoshaphat-Obol added a commit to Jehoshaphat-Obol/ui that referenced this issue Feb 17, 2025
This feature introduces a options on the theme generation page, allowing users to select their target Tailwind CSS version (<v4 or v4+).  This ensures that the generated CSS is compatible with the user's project.

Previously, the theme generator only produced CSS compatible with older Tailwind versions, which caused issues for users on v4.  This change addresses those compatibility problems.

The implementation involves modifying the CSS generation logic to handle the differences between Tailwind versions.  Specifically, v4 requires the use of `@theme inline` and `hsl()` wrappers for color values.  The selected version is used to determine the correct CSS syntax.

This change improves the user experience by providing a more flexible and robust theme generation process.
@flixlix
Copy link
Contributor

flixlix commented Feb 18, 2025

why is data-slot being used? you said it's for styling but I don't see any styling being applied based on this attribute in the codebase

@realizd-ai
Copy link

I'm following the Next.js instructions and getting this error on the second command:

#Start a new project with Tailwind v4 and React 19:
pnpm create next-app@canary --tailwind --eslint --typescript --app --no-src-dir # all good

#Init shadcn/ui. This will create your components.json and set up your CSS variables:
pnpm dlx shadcn@canary init
✔ Preflight checks.
✔ Verifying framework. Found Next.js.
✔ Validating Tailwind CSS config. Found v4.
✖ Validating import alias.

No import alias found in your tsconfig.json file.
Visit https://ui.shadcn.com/docs/installation/next to learn how to set an import alias.

@chahore
Copy link

chahore commented Feb 18, 2025

Is there a shadcn tailwind v4 monorepo example somewhere? Right now the instructions for TW4 seem to be for a standalone app, and the mono repo doc instructions are still creating a v3 mono repo setup.

just wondering if there is a documented way to achieve this, or an example repo somewhere?

I'm wondering if theres an update on this

@belmeopmenieuwesim
Copy link

belmeopmenieuwesim commented Feb 18, 2025

Nice to see support for TW4 🚀. Seems like the Button is not consistent across browsers. For example in in chrome/brave browser the button text is bolder than it is on Firefox. Also the button text seems to be not centered vertically (see image below). I checked and Shadcn with TW3 does not have these issues.

Image
^ Brave Browser

Image
^ Firefox

EDIT: Also it seems the hand pointer cursor is not being set on the Button. (EDIT: Just found a solution for this in the upgrade guide: https://tailwindcss.com/docs/upgrade-guide#buttons-use-the-default-cursor)

@subproject22
Copy link

why is data-slot being used? you said it's for styling but I don't see any styling being applied based on this attribute in the codebase

Probably just for the future.

@ihalaij1
Copy link

I don't know if this is intentional, but the Tabs component's color transition looks instant now (not smooth like before), because transition-all was changed to transition-[color,box-shadow] at some point.

This can be fixed by adding background-color like this transition-[color,background-color,box-shadow] to the two relevant lines here: https://github.com/shadcn-ui/ui/blob/main/apps/v4/registry/new-york-v4/ui/tabs.tsx#L45-L61

@Sparticuz
Copy link

Input.tsx has some duplicated classNames around the aria classnames.

aria-invalid:outline-destructive/60
aria-invalid:ring-destructive/20
dark:aria-invalid:outline-destructive
dark:aria-invalid:ring-destructive/50

@shadcn
Copy link
Collaborator Author

shadcn commented Feb 19, 2025

Update: I'm going through the comments and addressing the issues. Keep them coming and thank you.

@Sparticuz
Copy link

SidebarProvider is still using forwardRef

@Ali-Hussein-dev
Copy link

I ran pnpm dlx shadcn@canary init and chose the Nextjs monorepo option, but it uses Tailwind v4, isn't supported yet for monorepo?

@BrendanC23
Copy link

My components.json file has rsc: false but running npx shadcn@canary add updated the components and added a "use client" directive.

@elijaholmos
Copy link

I'm performing migrations manually. Is there any guide on how to migrate over my new components.json file? Preferably a new JSON schema link.

@ihalaij1
Copy link

ihalaij1 commented Feb 20, 2025

When navigating a page with the Tab key, the ring for the selected Card under TabsContent doesn't have rounded corners for some reason:

Image

@Ali-Hussein-dev
Copy link

The Geist font isn't working in the demo v4.shadcn.com

@bernaferrari
Copy link
Contributor

Not sure it is known, but clicking on sidebar edges is not making it smaller. Once you update blocks to tailwind 4/the new shadcn/ui, you will see this. I was using code similar to the first block and got this.

@Jordan-Edgington
Copy link

I had a fresh install for a new project. I get an error every time I add a component because adding a component puts

@layer base {
  * {
    @apply border-border outline-ring/50;
  }
  body {
    @apply bg-background text-foreground;
  }
}

in my index.css... however with fresh tailwind4 with shadcnui/canary instructions, there is no tailwind.config.js to define border-border and this causes an error. For now, working around by just manually deleting this block so there is no default styling on components. Just using my own inline styling on each component.

@Jacksonmills
Copy link
Contributor

Testing locally, is @custom-variant dark (&:is(.dark *)); really needed with the current setup? I just tested both ways and it seems it is not.

@kangpeter5
Copy link

kangpeter5 commented Feb 20, 2025

@shadcn So I'm using shadcn@canary with tailwindcss v4.0.7 but for some reason, the tailwindcss styling does not get fully applied to the shadcn components.

Button:
Image

Folder:
Image

@The-Best-Codes
Copy link

Buttons don't have a cursor-pointer

Buttons no longer have cursor: pointer; by default in Tailwind v4. In many browsers, shadcn buttons or clickable components which previously had a pointer cursor now use the default cursor.

To fix this, you can add this code to your Tailwind CSS file (e.g., index.css) as noted in the docs:

@layer base {
  button:not(:disabled),
  [role="button"]:not(:disabled) {
    cursor: pointer;
  }
}

You could also manually add cursor-pointer in components where applicable.

Tailwind docs on this change:
https://tailwindcss.com/docs/upgrade-guide#buttons-use-the-default-cursor

@kangpeter5
Copy link

Buttons don't have a cursor-pointer

Buttons no longer have cursor: pointer; by default in Tailwind v4. In many browsers, shadcn buttons or clickable components which previously had a pointer cursor now use the default cursor.

To fix this, you can add this code to your Tailwind CSS file (e.g., index.css) as noted in the docs:

@layer base {
button:not(:disabled),
[role="button"]:not(:disabled) {
cursor: pointer;
}
}
You could also manually add cursor-pointer in components where applicable.

Tailwind docs on this change: https://tailwindcss.com/docs/upgrade-guide#buttons-use-the-default-cursor

Not sure if you were responding to me, but my issues is for all components.

@The-Best-Codes
Copy link

@kangpeter5 I was not responding to you, sorry. 🙁
Just reporting the issue as the shadcn docs say to do:
https://ui.shadcn.com/docs/tailwind-v4#see-it-live

🙂

@kangpeter5
Copy link

Is there a shadcn tailwind v4 monorepo example somewhere? Right now the instructions for TW4 seem to be for a standalone app, and the mono repo doc instructions are still creating a v3 mono repo setup.

just wondering if there is a documented way to achieve this, or an example repo somewhere?

We're using turborepo and would love an update on this.

@CHC383
Copy link

CHC383 commented Feb 20, 2025

I'm performing migrations manually. Is there any guide on how to migrate over my new components.json file? Preferably a new JSON schema link.

@elijaholmos Here is the new components.json from the v4 website, you could find all the V4 code (css, config, components) in apps/v4 for reference

https://github.com/shadcn-ui/ui/blob/main/apps/v4/components.json

@emmmhx
Copy link

emmmhx commented Feb 21, 2025

For some reason borders are not applied to any component, this is my globals.css

@plugin "tailwindcss-animate";

@custom-variant dark (&:is(.dark *));

:root {
  --card: hsl(0 0% 100%);
  --card-foreground: hsl(222.2 84% 4.9%);
  --popover: hsl(0 0% 100%);
  --popover-foreground: hsl(222.2 84% 4.9%);
  --primary: hsl(222.2 47.4% 11.2%);
  --primary-foreground: hsl(210 40% 98%);
  --secondary: hsl(210 40% 96.1%);
  --secondary-foreground: hsl(222.2 47.4% 11.2%);
  --muted: hsl(210 40% 96.1%);
  --muted-foreground: hsl(215.4 16.3% 46.9%);
  --accent: hsl(210 40% 96.1%);
  --accent-foreground: hsl(222.2 47.4% 11.2%);
  --destructive: hsl(0 84.2% 60.2%);
  --destructive-foreground: hsl(210 40% 98%);
  --border: hsl(214.3 31.8% 91.4%);
  --input: hsl(214.3 31.8% 91.4%);
  --ring: hsl(222.2 84% 4.9%);
  --chart-1: hsl(12 76% 61%);
  --chart-2: hsl(173 58% 39%);
  --chart-3: hsl(197 37% 24%);
  --chart-4: hsl(43 74% 66%);
  --chart-5: hsl(27 87% 67%);
  --radius: 0.6rem;
  --sidebar: oklch(0.985 0 0);
  --sidebar-foreground: oklch(0.145 0 0);
  --sidebar-primary: oklch(0.205 0 0);
  --sidebar-primary-foreground: oklch(0.985 0 0);
  --sidebar-accent: oklch(0.97 0 0);
  --sidebar-accent-foreground: oklch(0.205 0 0);
  --sidebar-border: oklch(0.922 0 0);
  --sidebar-ring: oklch(0.87 0 0);
  --background: hsl(0 0% 100%);
  --foreground: hsl(222.2 84% 4.9%);
}

.dark {
  --background: hsl(222.2 84% 4.9%);
  --foreground: hsl(210 40% 98%);
  --card: hsl(222.2 84% 4.9%);
  --card-foreground: hsl(210 40% 98%);
  --popover: hsl(222.2 84% 4.9%);
  --popover-foreground: hsl(210 40% 98%);
  --primary: hsl(210 40% 98%);
  --primary-foreground: hsl(222.2 47.4% 11.2%);
  --secondary: hsl(217.2 32.6% 17.5%);
  --secondary-foreground: hsl(210 40% 98%);
  --muted: hsl(217.2 32.6% 17.5%);
  --muted-foreground: hsl(215 20.2% 65.1%);
  --accent: hsl(217.2 32.6% 17.5%);
  --accent-foreground: hsl(210 40% 98%);
  --destructive: hsl(0 62.8% 30.6%);
  --destructive-foreground: hsl(210 40% 98%);
  --border: hsl(217.2 32.6% 17.5%);
  --input: hsl(217.2 32.6% 17.5%);
  --ring: hsl(212.7 26.8% 83.9%);
  --chart-1: hsl(220 70% 50%);
  --chart-2: hsl(160 60% 45%);
  --chart-3: hsl(30 80% 55%);
  --chart-4: hsl(280 65% 60%);
  --chart-5: hsl(340 75% 55%);
  --sidebar: oklch(0.205 0 0);
  --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.269 0 0);
  --sidebar-accent-foreground: oklch(0.985 0 0);
  --sidebar-border: oklch(0.269 0 0);
  --sidebar-ring: oklch(0.439 0 0);
}

@theme inline {
  --font-sans: var(--font-geist-sans);
  --font-mono: var(--font-geist-mono);
  --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-destructive-foreground: var(--destructive-foreground);
  --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);
  --radius-sm: calc(var(--radius) - 4px);
  --radius-md: calc(var(--radius) - 2px);
  --radius-lg: var(--radius);
  --radius-xl: calc(var(--radius) + 4px);
  --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);
}

@layer base {
  * {
    @apply border-border outline-ring/50;
  }
  body {
    @apply bg-background text-foreground;
  }
}```

@shadcn-ui shadcn-ui locked and limited conversation to collaborators Feb 21, 2025
@shadcn shadcn converted this issue into discussion #6714 Feb 21, 2025

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Projects
None yet
Development

No branches or pull requests