Create project
First, create a new Next.js or React.js project using create-next-app
:
npx create-next-app@latest my-project --typescript --eslint --app
or Install React.js via Vite
:
npm create vite@latest my-react-app -- --template react
Add Tailwind CSS (v4)
I use Tailwind CSS v4. You need to install Tailwind CSS in your project.
If you are using Tailwind v3, then I recommend you to upgrade to v4 by using the following command:
npx @tailwindcss/upgrade
or install shadcn/ui
with tailwindcss v4:
npx shadcn@latest init
Add dependencies
Add the following dependencies to your project:
npm install framer-motion tailwindcss-animate class-variance-authority clsx tailwind-merge
Add icon library
For Icons, install the following dependencies:
npm install lucide-react @radix-ui/react-icons
Configure path aliases
I use the @
alias. This is how I configure it in tsconfig.json:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./*"]
}
}
}
The @
alias is a preference. You can use other aliases if you want.
If you use a different alias such as ~, you'll need to update import statements when adding components.
Configure styles
Add the following to your globals.css
file. You can learn more about using CSS variables for theming.
@import "tailwindcss";
@plugin 'tailwindcss-animate';
@custom-variant dark (&:where(.dark, .dark *));
:root {
--background: hsl(0 0% 100%);
--foreground: hsl(240 10% 3.9%);
--card: hsl(0 0% 100%);
--card-foreground: hsl(240 10% 3.9%);
--popover: hsl(0 0% 100%);
--popover-foreground: hsl(240 10% 3.9%);
--primary: hsl(240 5.9% 10%);
--primary-foreground: hsl(0 0% 98%);
--secondary: hsl(240 4.8% 95.9%);
--secondary-foreground: hsl(240 5.9% 10%);
--muted: hsl(240 4.8% 95.9%);
--muted-foreground: hsl(240 3.8% 46.1%);
--accent: hsl(240 4.8% 95.9%);
--accent-foreground: hsl(240 5.9% 10%);
--destructive: hsl(0 84.2% 60.2%);
--destructive-foreground: hsl(0 0% 98%);
--border: hsl(240 5.9% 90%);
--input: hsl(240 5.9% 90%);
--ring: hsl(240 5% 64.9%);
--chart-1: hsl(41 22.2% 64.6%);
--chart-2: hsl(185 11.8% 60%);
--chart-3: hsl(227 7% 39.8%);
--chart-4: hsl(84 18.9% 82.8%);
--chart-5: hsl(70 18.8% 76.9%);
--radius: 0.625rem;
--sidebar: hsl(0 0% 98.5%);
--sidebar-foreground: hsl(0 0% 14.5%);
--sidebar-primary: hsl(0 0% 20.5%);
--sidebar-primary-foreground: hsl(0 0% 98.5%);
--sidebar-accent: hsl(0 0% 97%);
--sidebar-accent-foreground: hsl(0 0% 20.5%);
--sidebar-border: hsl(0 0% 92.2%);
--sidebar-ring: hsl(0 0% 70.8%);
}
.dark {
--background: hsl(0 0% 0%);
--foreground: hsl(0 0% 98%);
--card: hsl(0 0% 0%);
--card-foreground: hsl(0 0% 98%);
--popover: hsl(0 0% 0%);
--popover-foreground: hsl(0 0% 98%);
--primary: hsl(0 0% 98.5%);
--primary-foreground: hsl(240 5.9% 10%);
--secondary: hsl(240 3.7% 15.9%);
--secondary-foreground: hsl(0 0% 98%);
--muted: hsl(240 3.7% 15.9%);
--muted-foreground: hsl(240 5% 64.9%);
--accent: hsl(240 3.7% 15.9%);
--accent-foreground: hsl(0 0% 98%);
--destructive: hsl(0 62.8% 30.6%);
--destructive-foreground: hsl(0 85.7% 97.3%);
--border: hsl(240 3.7% 15.9%);
--input: hsl(240 3.7% 15.9%);
--ring: hsl(240 4.9% 83.9%);
--chart-1: hsl(264 24.3% 48.8%);
--chart-2: hsl(162 17% 69.6%);
--chart-3: hsl(70 18.8% 76.9%);
--chart-4: hsl(304 26.5% 62.7%);
--chart-5: hsl(16 24.6% 64.5%);
--sidebar: hsl(240 3.7% 15.9%);
--sidebar-foreground: hsl(0 0% 98.5%);
--sidebar-primary: hsl(264 24.3% 48.8%);
--sidebar-primary-foreground: hsl(0 0% 98.5%);
--sidebar-accent: hsl(240 3.7% 15.9%);
--sidebar-accent-foreground: hsl(0 0% 98.5%);
--sidebar-border: hsl(240 3.7% 15.9%);
--sidebar-ring: hsl(240 3.7% 15.9%);
}
@theme inline {
--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;
}
}
Add a cn helper
I use a cn
helper to make it easier to conditionally add Tailwind CSS classes. Here's how I define it in lib/utils.ts
:
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
That's it
You can now start adding components to your project.