mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-07-07 22:18:39 +00:00
297 lines
7.7 KiB
Plaintext
297 lines
7.7 KiB
Plaintext
---
|
||
title: An update on Tailwind v4
|
||
description: How to use shadcn/ui with Tailwind v4.
|
||
---
|
||
|
||
It’s here! Tailwind v4 and React 19. Ready for you to try out. It's available in the `canary` release of the CLI. You can start using it today.
|
||
|
||
## TLDR
|
||
|
||
If you're starting a new project with Tailwind v4 and React 19, use the `canary` version of the command-line:
|
||
|
||
```bash
|
||
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 until you upgrade. Only new projects start with Tailwind v4 and React 19.**
|
||
|
||
## What's Coming Next
|
||
|
||
The following is still being worked on. I'll post updates soon.
|
||
|
||
- 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 on the [GitHub issue](https://github.com/shadcn-ui/ui/issues/6585).
|
||
|
||
- The code for the app is here: https://github.com/shadcn-ui/ui/tree/main/apps/v4
|
||
- The v4 components can be found here: https://github.com/shadcn-ui/ui/tree/main/apps/v4/registry/new-york-v4/ui
|
||
|
||
## Try It Out
|
||
|
||
You can test Tailwind v4 + React 19 today using the `canary` release of the CLI.
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
pnpm create next-app@canary --tailwind --eslint --typescript --app --no-src-dir
|
||
```
|
||
|
||
2. Init shadcn/ui. This will create your `components.json` and set up your CSS variables:
|
||
|
||
```bash
|
||
pnpm dlx shadcn@canary init
|
||
```
|
||
|
||
3. You should now be able to add components:
|
||
|
||
```bash
|
||
pnpm dlx shadcn@canary add button
|
||
```
|
||
|
||
### Vite
|
||
|
||
1. Create a new project with React 19:
|
||
|
||
```bash
|
||
pnpm create vite --template=react-ts
|
||
```
|
||
|
||
2. Follow the official guide to add Tailwind CSS: https://tailwindcss.com/docs/installation/using-vite
|
||
|
||
3. Add path aliases to `tsconfig.json`:
|
||
|
||
```json title="tsconfig.json" showLineNumbers
|
||
{
|
||
"files": [],
|
||
"references": [
|
||
{ "path": "./tsconfig.app.json" },
|
||
{ "path": "./tsconfig.node.json" }
|
||
],
|
||
"compilerOptions": {
|
||
"baseUrl": ".",
|
||
"paths": {
|
||
"@/*": ["./src/*"]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
4. Add path aliases to `tsconfig.app.json`:
|
||
|
||
```json title="tsconfig.app.json" showLineNumbers
|
||
{
|
||
"compilerOptions": {
|
||
"baseUrl": ".",
|
||
"paths": {
|
||
"@/*": ["./src/*"]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
5. Install `@types/node`:
|
||
|
||
```bash
|
||
pnpm add -D @types/node
|
||
```
|
||
|
||
6. Add resolve alias config to `vite.config.ts`:
|
||
|
||
```ts title="vite.config.ts" showLineNumbers
|
||
import path from "path"
|
||
import tailwindcss from "@tailwindcss/vite"
|
||
import react from "@vitejs/plugin-react"
|
||
import { defineConfig } from "vite"
|
||
|
||
export default defineConfig({
|
||
plugins: [react(), tailwindcss()],
|
||
resolve: {
|
||
alias: {
|
||
"@": path.resolve(__dirname, "./src"),
|
||
},
|
||
},
|
||
})
|
||
```
|
||
|
||
7. Init `shadcn/ui`. This will create your `components.json` and set up your CSS variables:
|
||
|
||
```bash
|
||
pnpm dlx shadcn@canary init
|
||
```
|
||
|
||
8. You should now be able to add components:
|
||
|
||
```bash
|
||
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.
|
||
|
||
```css showLineNumbers
|
||
@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`
|
||
|
||
```css showLineNumbers
|
||
:root {
|
||
--background: hsl(0 0% 100%); // <-- Wrap in hsl
|
||
--foreground: hsl(0 0% 3.9%);
|
||
}
|
||
|
||
dark {
|
||
--background: hsl(0 0% 3.9%); // <-- Wrap in hsl
|
||
--foreground: hsl(0 0% 98%);
|
||
}
|
||
|
||
@theme inline {
|
||
--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`:
|
||
|
||
```diff
|
||
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
|
||
```
|
||
|
||
### 4. 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:
|
||
|
||
```diff
|
||
- w-4 h-4
|
||
+ size-4
|
||
```
|
||
|
||
### 5. Update your dependencies
|
||
|
||
```bash
|
||
pnpm up "@radix-ui/*" cmdk lucide-react recharts tailwind-merge clsx --latest
|
||
```
|
||
|
||
### 6. 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
|
||
|
||
```tsx showLineNumbers
|
||
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
|
||
|
||
```tsx showLineNumbers
|
||
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.
|