Compare commits

...

19 Commits

Author SHA1 Message Date
github-actions[bot]
cd9a55b76a chore(release): version packages (#4777)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-09-07 23:42:29 +04:00
shadcn
49373eed96 fix(shadcn): better error handling (#4776)
* fix(shadcn): better error messages

* chore: changeset
2024-09-07 23:35:37 +04:00
shadcn
078dfe6607 docs(www): Open in v0 docs (#4734)
* feat(www): open in v0

* feat: update copy

* fix: sidebar link

* fix(tests): snapshots
2024-09-04 00:37:57 +04:00
xuxucode
77fc5ec8db Fix use-toast module path (#4728) 2024-09-03 18:37:11 +00:00
github-actions[bot]
cfba3fdf70 chore(release): version packages (#4730)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-09-03 22:22:26 +04:00
adrianhelvikspond
4e4118f3cf Fix init command for default style - fixes #4722 (#4724)
* Fix init command for default style

The empty dependency string was tripping up package managers.

* fix(www): registry dependencies for default

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-09-03 22:21:51 +04:00
shadcn
faa7a67fb3 fix(shadcn): init with src (#4731)
* fix(shadcn): init with src

* chore: add changesets
2024-09-03 22:16:59 +04:00
shadcn
701e1160ea feat(shadcn): add support for src dir (#4729)
* feat(shadcn): add support for src dir

* chore: add changesets
2024-09-03 21:47:15 +04:00
shadcn
f5931f8d09 docs(www): update installation docs (#4725) 2024-09-03 16:41:48 +04:00
shadcn
5a28937c6e Merge branch 'main' of github.com:shadcn/ui 2024-09-03 16:35:51 +04:00
shadcn
0b74059d38 docs(www): update laravel docs 2024-09-03 16:35:36 +04:00
shadcn
fab9877586 fix(shadcn): handle quote in theme values (#4726)
* fix(shadcn): handle quote in theme values

* chore(shadcn): fix theme values bug
2024-09-03 16:22:45 +04:00
shadcn
0f7591f67c docs(www): updates docs for Astro (#4723) 2024-09-03 15:35:24 +04:00
shadcn
81c7e44863 fix(www): url 2024-08-31 03:15:10 +04:00
shadcn
2fac3e40c2 fix(www): hide sidebar-01 for now 2024-08-31 03:10:14 +04:00
shadcn
5ad11ff851 docs(www): update callout 2024-08-31 03:01:42 +04:00
shadcn
6b92dd8eaf Merge branch 'main' of github.com:shadcn/ui 2024-08-31 02:57:49 +04:00
shadcn
84540f551d fix: blocks 2024-08-31 02:57:37 +04:00
shadcn
99588fff8f fix: cli (#4669) 2024-08-31 02:09:14 +04:00
34 changed files with 464 additions and 262 deletions

View File

@@ -0,0 +1,13 @@
import { LoginForm } from "@/registry/default/block/login-01/components/login-form"
export const iframeHeight = "870px"
export const containerClassName = "w-full h-full"
export default function Page() {
return (
<div className="flex h-screen w-full items-center justify-center px-4">
<LoginForm />
</div>
)
}

View File

@@ -0,0 +1,25 @@
import { AppSidebar } from "@/registry/default/block/sidebar-01/components/app-sidebar"
import {
SidebarLayout,
SidebarTrigger,
} from "@/registry/default/block/sidebar-01/ui/sidebar"
export const iframeHeight = "870px"
export const containerClassName = "w-full h-full"
export default async function Page() {
const { cookies } = await import("next/headers")
return (
<SidebarLayout
defaultOpen={cookies().get("sidebar:state")?.value === "true"}
>
<AppSidebar />
<main className="flex flex-1 flex-col p-2 transition-all duration-300 ease-in-out">
<div className="h-full rounded-md border-2 border-dashed p-2">
<SidebarTrigger />
</div>
</main>
</SidebarLayout>
)
}

View File

@@ -2145,7 +2145,7 @@ export const Index: Record<string, any> = {
registryDependencies: ["avatar","button","collapsible","dropdown-menu","drawer","separator","input","popover","sheet","progress","card","use-mobile"], registryDependencies: ["avatar","button","collapsible","dropdown-menu","drawer","separator","input","popover","sheet","progress","card","use-mobile"],
files: ["registry/new-york/block/sidebar-01/page.tsx","registry/new-york/block/sidebar-01/components/app-sidebar.tsx","registry/new-york/block/sidebar-01/components/nav-main.tsx","registry/new-york/block/sidebar-01/components/nav-projects.tsx","registry/new-york/block/sidebar-01/components/nav-secondary.tsx","registry/new-york/block/sidebar-01/components/nav-user.tsx","registry/new-york/block/sidebar-01/components/storage-card.tsx","registry/new-york/block/sidebar-01/components/team-switcher.tsx","registry/new-york/block/sidebar-01/hooks/use-sidebar.tsx","registry/new-york/block/sidebar-01/ui/sidebar.tsx"], files: ["registry/new-york/block/sidebar-01/page.tsx","registry/new-york/block/sidebar-01/components/app-sidebar.tsx","registry/new-york/block/sidebar-01/components/nav-main.tsx","registry/new-york/block/sidebar-01/components/nav-projects.tsx","registry/new-york/block/sidebar-01/components/nav-secondary.tsx","registry/new-york/block/sidebar-01/components/nav-user.tsx","registry/new-york/block/sidebar-01/components/storage-card.tsx","registry/new-york/block/sidebar-01/components/team-switcher.tsx","registry/new-york/block/sidebar-01/hooks/use-sidebar.tsx","registry/new-york/block/sidebar-01/ui/sidebar.tsx"],
component: React.lazy(() => import("@/registry/new-york/block/sidebar-01/page.tsx")), component: React.lazy(() => import("@/registry/new-york/block/sidebar-01/page.tsx")),
source: "__registry__/new-york/block/sidebar-01.tsx", source: "__registry__/new-york/block/sidebar-01/page.tsx",
category: "Application", category: "Application",
subcategory: "Dashboard", subcategory: "Dashboard",
chunks: [] chunks: []
@@ -2156,7 +2156,7 @@ export const Index: Record<string, any> = {
registryDependencies: ["button","card","input","label"], registryDependencies: ["button","card","input","label"],
files: ["registry/new-york/block/login-01/page.tsx","registry/new-york/block/login-01/components/login-form.tsx"], files: ["registry/new-york/block/login-01/page.tsx","registry/new-york/block/login-01/components/login-form.tsx"],
component: React.lazy(() => import("@/registry/new-york/block/login-01/page.tsx")), component: React.lazy(() => import("@/registry/new-york/block/login-01/page.tsx")),
source: "__registry__/new-york/block/login-01.tsx", source: "__registry__/new-york/block/login-01/page.tsx",
category: "Authentication", category: "Authentication",
subcategory: "Login", subcategory: "Login",
chunks: [] chunks: []
@@ -5501,7 +5501,7 @@ export const Index: Record<string, any> = {
registryDependencies: ["avatar","button","collapsible","dropdown-menu","drawer","separator","input","popover","sheet","progress","card","use-mobile"], registryDependencies: ["avatar","button","collapsible","dropdown-menu","drawer","separator","input","popover","sheet","progress","card","use-mobile"],
files: ["registry/default/block/sidebar-01/page.tsx","registry/default/block/sidebar-01/components/app-sidebar.tsx","registry/default/block/sidebar-01/components/nav-main.tsx","registry/default/block/sidebar-01/components/nav-projects.tsx","registry/default/block/sidebar-01/components/nav-secondary.tsx","registry/default/block/sidebar-01/components/nav-user.tsx","registry/default/block/sidebar-01/components/storage-card.tsx","registry/default/block/sidebar-01/components/team-switcher.tsx","registry/default/block/sidebar-01/hooks/use-sidebar.tsx","registry/default/block/sidebar-01/ui/sidebar.tsx"], files: ["registry/default/block/sidebar-01/page.tsx","registry/default/block/sidebar-01/components/app-sidebar.tsx","registry/default/block/sidebar-01/components/nav-main.tsx","registry/default/block/sidebar-01/components/nav-projects.tsx","registry/default/block/sidebar-01/components/nav-secondary.tsx","registry/default/block/sidebar-01/components/nav-user.tsx","registry/default/block/sidebar-01/components/storage-card.tsx","registry/default/block/sidebar-01/components/team-switcher.tsx","registry/default/block/sidebar-01/hooks/use-sidebar.tsx","registry/default/block/sidebar-01/ui/sidebar.tsx"],
component: React.lazy(() => import("@/registry/default/block/sidebar-01/page.tsx")), component: React.lazy(() => import("@/registry/default/block/sidebar-01/page.tsx")),
source: "__registry__/default/block/sidebar-01.tsx", source: "__registry__/default/block/sidebar-01/page.tsx",
category: "Application", category: "Application",
subcategory: "Dashboard", subcategory: "Dashboard",
chunks: [] chunks: []
@@ -5512,7 +5512,7 @@ export const Index: Record<string, any> = {
registryDependencies: ["button","card","input","label"], registryDependencies: ["button","card","input","label"],
files: ["registry/default/block/login-01/page.tsx","registry/default/block/login-01/components/login-form.tsx"], files: ["registry/default/block/login-01/page.tsx","registry/default/block/login-01/components/login-form.tsx"],
component: React.lazy(() => import("@/registry/default/block/login-01/page.tsx")), component: React.lazy(() => import("@/registry/default/block/login-01/page.tsx")),
source: "__registry__/default/block/login-01.tsx", source: "__registry__/default/block/login-01/page.tsx",
category: "Authentication", category: "Authentication",
subcategory: "Login", subcategory: "Login",
chunks: [] chunks: []

View File

@@ -0,0 +1,13 @@
import { LoginForm } from "@/registry/new-york/block/login-01/components/login-form"
export const iframeHeight = "870px"
export const containerClassName = "w-full h-full"
export default function Page() {
return (
<div className="flex h-screen w-full items-center justify-center px-4">
<LoginForm />
</div>
)
}

View File

@@ -0,0 +1,25 @@
import { AppSidebar } from "@/registry/new-york/block/sidebar-01/components/app-sidebar"
import {
SidebarLayout,
SidebarTrigger,
} from "@/registry/new-york/block/sidebar-01/ui/sidebar"
export const iframeHeight = "870px"
export const containerClassName = "w-full h-full"
export default async function Page() {
const { cookies } = await import("next/headers")
return (
<SidebarLayout
defaultOpen={cookies().get("sidebar:state")?.value === "true"}
>
<AppSidebar />
<main className="flex flex-1 flex-col p-2 transition-all duration-300 ease-in-out">
<div className="h-full rounded-md border-2 border-dashed p-2">
<SidebarTrigger />
</div>
</main>
</SidebarLayout>
)
}

View File

@@ -5,7 +5,10 @@ import { ThemesSwitcher } from "@/components/themes-selector"
export default async function BlocksPage() { export default async function BlocksPage() {
const blocks = (await getAllBlockIds()).filter( const blocks = (await getAllBlockIds()).filter(
(name) => !name.startsWith("chart-") (name) =>
!name.startsWith("chart-") &&
!name.startsWith("sidebar-01") &&
!name.startsWith("login-01")
) )
// These themes are not compatible with the blocks yet. // These themes are not compatible with the blocks yet.

View File

@@ -11,6 +11,7 @@ import { siteConfig } from "@/config/site"
import { getTableOfContents } from "@/lib/toc" import { getTableOfContents } from "@/lib/toc"
import { absoluteUrl, cn } from "@/lib/utils" import { absoluteUrl, cn } from "@/lib/utils"
import { Mdx } from "@/components/mdx-components" import { Mdx } from "@/components/mdx-components"
import { OpenInV0Cta } from "@/components/open-in-v0-cta"
import { DocsPager } from "@/components/pager" import { DocsPager } from "@/components/pager"
import { DashboardTableOfContents } from "@/components/toc" import { DashboardTableOfContents } from "@/components/toc"
import { badgeVariants } from "@/registry/new-york/ui/badge" import { badgeVariants } from "@/registry/new-york/ui/badge"
@@ -135,17 +136,14 @@ export default async function DocPage({ params }: DocPageProps) {
</div> </div>
<DocsPager doc={doc} /> <DocsPager doc={doc} />
</div> </div>
{doc.toc && ( <div className="hidden text-sm xl:block">
<div className="hidden text-sm xl:block"> <div className="sticky top-16 -mt-10 h-[calc(100vh-3.5rem)] pt-4">
<div className="sticky top-16 -mt-10 pt-4"> <ScrollArea className="h-full pb-10">
<ScrollArea className="pb-10"> {doc.toc && <DashboardTableOfContents toc={toc} />}
<div className="sticky top-16 -mt-10 h-[calc(100vh-3.5rem)] py-12"> <OpenInV0Cta className="mt-6 max-w-[80%]" />
<DashboardTableOfContents toc={toc} /> </ScrollArea>
</div>
</ScrollArea>
</div>
</div> </div>
)} </div>
</main> </main>
) )
} }

View File

@@ -7,7 +7,7 @@ import { Separator } from "@/registry/new-york/ui/separator"
export function Announcement() { export function Announcement() {
return ( return (
<Link <Link
href="/docs/components/chart" href="/docs/changelog"
className="group inline-flex items-center px-0.5 text-sm font-medium" className="group inline-flex items-center px-0.5 text-sm font-medium"
> >
<PieChart className="h-4 w-4" />{" "} <PieChart className="h-4 w-4" />{" "}

View File

@@ -0,0 +1,35 @@
import Link from "next/link"
import { cn } from "@/lib/utils"
import { Button } from "@/registry/new-york/ui/button"
export function OpenInV0Cta({ className }: React.ComponentProps<"div">) {
return (
<div
className={cn(
"group relative flex flex-col gap-2 rounded-lg border p-4 text-sm",
className
)}
>
<div className="text-balance text-lg font-semibold leading-tight group-hover:underline">
Bring your app built with shadcn to life on Vercel
</div>
<div>Trusted by OpenAI, Sonos, Chick-fil-A, and more.</div>
<div>
Vercel provides tools and infrastructure to deploy apps and features at
scale.
</div>
<Button size="sm" className="mt-2 w-fit">
Deploy Now
</Button>
<Link
href="https://vercel.com/new?utm_source=shadcn_site&utm_medium=web&utm_campaign=docs_cta_deploy_now_callout"
target="_blank"
rel="noreferrer"
className="absolute inset-0"
>
<span className="sr-only">Deploy to Vercel</span>
</Link>
</div>
)
}

View File

@@ -26,7 +26,7 @@ export function DashboardTableOfContents({ toc }: TocProps) {
const activeHeading = useActiveItem(itemIds) const activeHeading = useActiveItem(itemIds)
const mounted = useMounted() const mounted = useMounted()
if (!toc?.items || !mounted) { if (!toc?.items?.length) {
return null return null
} }

View File

@@ -77,6 +77,12 @@ export const docsConfig: DocsConfig = {
href: "/docs/components/typography", href: "/docs/components/typography",
items: [], items: [],
}, },
{
title: "Open in v0",
href: "/docs/v0",
items: [],
label: "New",
},
{ {
title: "Figma", title: "Figma",
href: "/docs/figma", href: "/docs/figma",
@@ -89,6 +95,46 @@ export const docsConfig: DocsConfig = {
}, },
], ],
}, },
{
title: "Installation",
items: [
{
title: "Next.js",
href: "/docs/installation/next",
items: [],
},
{
title: "Vite",
href: "/docs/installation/vite",
items: [],
},
{
title: "Remix",
href: "/docs/installation/remix",
items: [],
},
{
title: "Astro",
href: "/docs/installation/astro",
items: [],
},
{
title: "Laravel",
href: "/docs/installation/laravel",
items: [],
},
{
title: "Gatsby",
href: "/docs/installation/gatsby",
items: [],
},
{
title: "Manual",
href: "/docs/installation/manual",
items: [],
},
],
},
{ {
title: "Components", title: "Components",
items: [ items: [

View File

@@ -5,7 +5,7 @@ description: Use the CLI to add components to your project.
<Callout> <Callout>
**Note:** We just released a new `shadcn` CLI to make it easier to add components to your project. See the [changelog](/docs/changelog) for more information. **Note:** We just released a new `shadcn` CLI. See the [changelog](/docs/changelog) for more information.
</Callout> </Callout>

View File

@@ -104,7 +104,7 @@ export default function RootLayout({ children }) {
The `useToast` hook returns a `toast` function that you can use to display a toast. The `useToast` hook returns a `toast` function that you can use to display a toast.
```tsx ```tsx
import { useToast } from "@/components/ui/use-toast" import { useToast } from "@/components/hooks/use-toast"
``` ```
```tsx {2,7-10} ```tsx {2,7-10}

View File

@@ -18,18 +18,12 @@ npm create astro@latest
You will be asked a few questions to configure your project: You will be asked a few questions to configure your project:
```txt showLineNumbers ```txt showLineNumbers
- Where should we create your new project? - Where should we create your new project? ./your-app-name
./your-app-name - How would you like to start your new project? Choose a template
- How would you like to start your new project? - Do you plan to write TypeScript? Yes
Choose a starter template (or Empty) - How strict should TypeScript be? Strict
- Install dependencies? - Install dependencies? Yes
Yes - Initialize a new git repository? (optional) Yes/No
- Do you plan to write TypeScript?
Yes
- How strict should TypeScript be?
Strict
- Initialize a new git repository? (optional)
Yes/No
``` ```
### Add React to your project ### Add React to your project
@@ -48,23 +42,47 @@ Answer `Yes` to all the question prompted by the CLI when installing React.
### Add Tailwind CSS to your project ### Add Tailwind CSS to your project
Install Tailwind CSS using the Astro CLI:
```bash ```bash
npx astro add tailwind npx astro add tailwind
``` ```
<Callout className="mt-4"> <Step>Create a `styles/globals.css` file in the `src` folder.</Step>
Answer `Yes` to all the question prompted by the CLI when installing Tailwind CSS. ```css title="styles/globals.css" showLineNumbers
@tailwind base;
@tailwind components;
@tailwind utilities;
```
</Callout> <Step>Import the `globals.css` file</Step>
Import the `styles/globals.css` file in the `src/pages/index.astro` file:
```ts title="src/pages/index.astro" showLineNumbers
---
import '@/styles/globals.css'
---
```
<Step>Update `astro.config.mjs` and set `applyBaseStyles` to `false`</Step>
To prevent serving the Tailwind base styles twice, we need to tell Astro not to apply the base styles, since we already include them in our own `globals.css` file. To do this, set the `applyBaseStyles` config option for the tailwind plugin in `astro.config.mjs` to `false`.
```js title="astro.config.mjs" {3-5} showLineNumbers
export default defineConfig({
integrations: [
tailwind({
applyBaseStyles: false,
}),
],
})
```
### Edit tsconfig.json file ### Edit tsconfig.json file
Add the following code to the `tsconfig.json` file to resolve paths: Add the following code to the `tsconfig.json` file to resolve paths:
```ts {4-9} showLineNumbers ```ts title="tsconfig.json" {4-9} showLineNumbers
{ {
"compilerOptions": { "compilerOptions": {
// ... // ...
@@ -81,62 +99,12 @@ Add the following code to the `tsconfig.json` file to resolve paths:
### Run the CLI ### Run the CLI
Run the `shadcn-ui` init command to setup your project: Run the `shadcn` init command to setup your project:
```bash ```bash
npx shadcn@latest init npx shadcn@latest init
``` ```
### Configure components.json
You will be asked a few questions to configure `components.json`:
```txt showLineNumbers
Would you like to use TypeScript (recommended)? no / yes
Which style would you like to use? Default
Which color would you like to use as base color? Slate
Where is your global CSS file? ./src/styles/globals.css
Do you want to use CSS variables for colors? no / yes
Where is your tailwind.config.js located? tailwind.config.mjs
Configure the import alias for components: @/components
Configure the import alias for utils: @/lib/utils
Are you using React Server Components? no
```
### Import the globals.css file
Import the `globals.css` file in the `src/pages/index.astro` file:
```ts {2} showLineNumbers
---
import '@/styles/globals.css'
---
```
### Update astro tailwind config
To prevent serving the Tailwind base styles twice, we need to tell Astro not to apply the base styles, since we already include them in our own `globals.css` file. To do this, set the `applyBaseStyles` config option for the tailwind plugin in `astro.config.mjs` to `false`.
```ts {3-5} showLineNumbers
export default defineConfig({
integrations: [
tailwind({
applyBaseStyles: false,
}),
],
})
```
### Update tailwind.config.mjs
When running `npx shadcn@latest init`, your tailwind config for content will be overwritten. To fix this, change the `module.exports` to `export default` and the `content` section with the code below to your `tailwind.config.mjs` file:
```js {1-4} showLineNumbers
export default {
content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"],
}
```
### That's it ### That's it
You can now start adding components to your project. You can now start adding components to your project.

View File

@@ -45,19 +45,6 @@ description: How to install dependencies and structure your app.
</svg> </svg>
<p className="font-medium mt-2">Remix</p> <p className="font-medium mt-2">Remix</p>
</LinkedCard> </LinkedCard>
<LinkedCard href="/docs/installation/gatsby">
<svg
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
className="w-10 h-10"
fill="currentColor"
>
<title>Gatsby</title>
<path d="M12 0C5.4 0 0 5.4 0 12s5.4 12 12 12 12-5.4 12-12S18.6 0 12 0zm0 2.571c3.171 0 5.915 1.543 7.629 3.858l-1.286 1.115C16.886 5.572 14.571 4.286 12 4.286c-3.343 0-6.171 2.143-7.286 5.143l9.857 9.857c2.486-.857 4.373-3 4.973-5.572h-4.115V12h6c0 4.457-3.172 8.228-7.372 9.17L2.83 9.944C3.772 5.743 7.543 2.57 12 2.57zm-9.429 9.6l9.344 9.258c-2.4-.086-4.801-.943-6.601-2.743-1.8-1.8-2.743-4.201-2.743-6.515z" />
</svg>
<p className="font-medium mt-2">Gatsby</p>
</LinkedCard>
<LinkedCard href="/docs/installation/astro"> <LinkedCard href="/docs/installation/astro">
<svg <svg
role="img" role="img"
@@ -86,6 +73,19 @@ description: How to install dependencies and structure your app.
</svg> </svg>
<p className="font-medium mt-2">Laravel</p> <p className="font-medium mt-2">Laravel</p>
</LinkedCard> </LinkedCard>
<LinkedCard href="/docs/installation/gatsby">
<svg
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
className="w-10 h-10"
fill="currentColor"
>
<title>Gatsby</title>
<path d="M12 0C5.4 0 0 5.4 0 12s5.4 12 12 12 12-5.4 12-12S18.6 0 12 0zm0 2.571c3.171 0 5.915 1.543 7.629 3.858l-1.286 1.115C16.886 5.572 14.571 4.286 12 4.286c-3.343 0-6.171 2.143-7.286 5.143l9.857 9.857c2.486-.857 4.373-3 4.973-5.572h-4.115V12h6c0 4.457-3.172 8.228-7.372 9.17L2.83 9.944C3.772 5.743 7.543 2.57 12 2.57zm-9.429 9.6l9.344 9.258c-2.4-.086-4.801-.943-6.601-2.743-1.8-1.8-2.743-4.201-2.743-6.515z" />
</svg>
<p className="font-medium mt-2">Gatsby</p>
</LinkedCard>
<LinkedCard href="/docs/installation/manual"> <LinkedCard href="/docs/installation/manual">
<svg <svg
role="img" role="img"

View File

@@ -15,7 +15,7 @@ laravel new my-app --typescript --breeze --stack=react --git --no-interaction
### Run the CLI ### Run the CLI
Run the `shadcn-ui` init command to setup your project: Run the `shadcn` init command to setup your project:
```bash ```bash
npx shadcn@latest init npx shadcn@latest init
@@ -26,106 +26,9 @@ npx shadcn@latest init
You will be asked a few questions to configure `components.json`: You will be asked a few questions to configure `components.json`:
```txt showLineNumbers ```txt showLineNumbers
Would you like to use TypeScript (recommended)? no / yes Which style would you like to use?
Which style would you like to use? Default Which color would you like to use as base color?
Which color would you like to use as base color? Slate Do you want to use CSS variables for colors? yes
Where is your global CSS file? resources/css/app.css
Do you want to use CSS variables for colors? no / yes
Where is your tailwind.config.js located? tailwind.config.js
Configure the import alias for components: @/Components
Configure the import alias for utils: @/lib/utils
Are you using React Server Components? no / yes
```
### Update tailwind.config.js
The `shadcn-ui` CLI will automatically overwrite your `tailwind.config.js`. Update it to look like this:
```js
import forms from "@tailwindcss/forms"
import defaultTheme from "tailwindcss/defaultTheme"
/** @type {import('tailwindcss').Config} */
export default {
darkMode: "class",
content: [
"./vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php",
"./storage/framework/views/*.php",
"./resources/views/**/*.blade.php",
"./resources/js/**/*.tsx",
],
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px",
},
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
},
borderRadius: {
lg: `var(--radius)`,
md: `calc(var(--radius) - 2px)`,
sm: "calc(var(--radius) - 4px)",
},
fontFamily: {
sans: ["Figtree", ...defaultTheme.fontFamily.sans],
},
keyframes: {
"accordion-down": {
from: { height: 0 },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: 0 },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
},
},
},
plugins: [forms, require("tailwindcss-animate")],
}
``` ```
### That's it ### That's it

View File

@@ -13,6 +13,16 @@ Run the `init` command to create a new Next.js project or to setup an existing o
npx shadcn@latest init npx shadcn@latest init
``` ```
<Callout className="mt-4">
You can use the `-d` flag for defaults i.e `new-york`, `zinc` and `yes` for the css variables.
```bash
npx shadcn@latest init -d
```
</Callout>
### Configure components.json ### Configure components.json
You will be asked a few questions to configure `components.json`: You will be asked a few questions to configure `components.json`:
@@ -23,40 +33,6 @@ Which color would you like to use as base color? Zinc
Do you want to use CSS variables for colors? no / yes Do you want to use CSS variables for colors? no / yes
``` ```
### App structure
Here's how I structure my Next.js apps. You can use this as a reference:
```txt {6-10,14-15}
.
├── app
│ ├── layout.tsx
│ └── page.tsx
├── components
│ ├── ui
│ │ ├── alert-dialog.tsx
│ │ ├── button.tsx
│ │ ├── dropdown-menu.tsx
│ │ └── ...
│ ├── main-nav.tsx
│ ├── page-header.tsx
│ └── ...
├── lib
│ └── utils.ts
├── styles
│ └── globals.css
├── next.config.js
├── package.json
├── postcss.config.js
├── tailwind.config.ts
└── tsconfig.json
```
- I place the UI components in the `components/ui` folder.
- The rest of the components such as `<PageHeader />` and `<MainNav />` are placed in the `components` folder.
- The `lib` folder contains all the utility functions. I have a `utils.ts` where I define the `cn` helper.
- The `styles` folder contains the global CSS.
### That's it ### That's it
You can now start adding components to your project. You can now start adding components to your project.

View File

@@ -0,0 +1,30 @@
---
title: Open in v0
description: Open components in v0 for customization.
---
Every component on ui.shadcn.com is editable on [v0 by Vercel](https://v0.dev). This allows you to easily customize the components in natural language and paste into your app.
<a href="https://vercel.com/signup?utm_source=shad&utm_medium=web&utm_campaign=docs_cta_signup">
<Image
src="/images/open-in-v0.png"
width="716"
height="420"
alt="Open in v0"
className="border dark:hidden shadow-sm rounded-lg overflow-hidden mt-6 w-full"
/>
<Image
src="/images/open-in-v0-dark.png"
width="716"
height="420"
alt="Open in v0"
className="border hidden dark:block shadow-sm rounded-lg overflow-hidden mt-6 w-full"
/>
<span class="sr-only">Open in v0</span>
</a>
To use v0, sign-up for a free [Vercel account here](https://vercel.com/signup?utm_source=shad&utm_medium=web&utm_campaign=docs_cta_signup). In addition to v0, this gives you free access to Vercel's frontend cloud platform by the creators of Next.js, where you can deploy and host your project for free.
Learn more about getting started with [Vercel here](https://vercel.com/docs/getting-started-with-vercel?utm_source=shadcn_site&utm_medium=web&utm_campaign=docs_cta_about_vercel).
Learn more about getting started with [v0 here](https://v0.dev/faq).

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -4,8 +4,7 @@
"dependencies": [ "dependencies": [
"tailwindcss-animate", "tailwindcss-animate",
"class-variance-authority", "class-variance-authority",
"lucide-react", "lucide-react"
""
], ],
"registryDependencies": [ "registryDependencies": [
"utils" "utils"

View File

@@ -231,6 +231,18 @@ export const Index: Record<string, any> = {
// // Write the source file for blocks only. // // Write the source file for blocks only.
sourceFilename = `__registry__/${style.name}/${type}/${item.name}.tsx` sourceFilename = `__registry__/${style.name}/${type}/${item.name}.tsx`
if (item.files) {
const files = item.files.map((file) =>
typeof file === "string"
? { type: "registry:page", path: file }
: file
)
if (files?.length) {
sourceFilename = `__registry__/${style.name}/${files[0].path}`
}
}
const sourcePath = path.join(process.cwd(), sourceFilename) const sourcePath = path.join(process.cwd(), sourceFilename)
if (!existsSync(sourcePath)) { if (!existsSync(sourcePath)) {
await fs.mkdir(sourcePath, { recursive: true }) await fs.mkdir(sourcePath, { recursive: true })
@@ -414,16 +426,21 @@ async function buildStylesIndex() {
for (const style of styles) { for (const style of styles) {
const targetPath = path.join(REGISTRY_PATH, "styles", style.name) const targetPath = path.join(REGISTRY_PATH, "styles", style.name)
const dependencies = [
"tailwindcss-animate",
"class-variance-authority",
"lucide-react",
]
// TODO: Remove this when we migrate to lucide-react.
if (style.name === "new-york") {
dependencies.push("@radix-ui/react-icons")
}
const payload: RegistryEntry = { const payload: RegistryEntry = {
name: style.name, name: style.name,
type: "registry:style", type: "registry:style",
dependencies: [ dependencies,
"tailwindcss-animate",
"class-variance-authority",
"lucide-react",
// TODO: Remove this when we migrate to lucide-react.
style.name === "new-york" ? "@radix-ui/react-icons" : "",
],
registryDependencies: ["utils"], registryDependencies: ["utils"],
tailwind: { tailwind: {
config: { config: {

View File

@@ -25,6 +25,7 @@
"shadcn:dev": "turbo --filter=shadcn dev", "shadcn:dev": "turbo --filter=shadcn dev",
"shadcn": "pnpm --filter=shadcn start:dev", "shadcn": "pnpm --filter=shadcn start:dev",
"shadcn:build": "pnpm --filter=shadcn build", "shadcn:build": "pnpm --filter=shadcn build",
"shadcn:test": "pnpm --filter=shadcn test",
"docs:build": "pnpm --filter=www build:docs", "docs:build": "pnpm --filter=www build:docs",
"www:dev": "pnpm --filter=www dev", "www:dev": "pnpm --filter=www dev",
"www:build": "pnpm --filter=www build", "www:build": "pnpm --filter=www build",

View File

@@ -1,5 +1,19 @@
# @shadcn/ui # @shadcn/ui
## 2.0.4
### Patch Changes
- [#4776](https://github.com/shadcn-ui/ui/pull/4776) [`49373ee`](https://github.com/shadcn-ui/ui/commit/49373eed9672d6ecf82219f6e682cab914e7cc41) Thanks [@shadcn](https://github.com/shadcn)! - better error handling
## 2.0.3
### Patch Changes
- [#4729](https://github.com/shadcn-ui/ui/pull/4729) [`701e116`](https://github.com/shadcn-ui/ui/commit/701e1160ea86b2ddd9f43121b447477caaf7aefa) Thanks [@shadcn](https://github.com/shadcn)! - add --src-dir
- [#4731](https://github.com/shadcn-ui/ui/pull/4731) [`faa7a67`](https://github.com/shadcn-ui/ui/commit/faa7a67fb30c741c2f2559fc64b34f103aac9d79) Thanks [@shadcn](https://github.com/shadcn)! - fix routes for src dir
## 2.0.0 ## 2.0.0
### Major Changes ### Major Changes

View File

@@ -1,6 +1,6 @@
{ {
"name": "shadcn", "name": "shadcn",
"version": "2.0.0", "version": "2.0.4",
"description": "Add components to your apps.", "description": "Add components to your apps.",
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
@@ -13,7 +13,7 @@
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/shadcn/ui.git", "url": "https://github.com/shadcn/ui.git",
"directory": "packages/cli" "directory": "packages/shadcn"
}, },
"files": [ "files": [
"dist" "dist"

View File

@@ -21,6 +21,7 @@ export const addOptionsSchema = z.object({
all: z.boolean(), all: z.boolean(),
path: z.string().optional(), path: z.string().optional(),
silent: z.boolean(), silent: z.boolean(),
srcDir: z.boolean().optional(),
}) })
export const add = new Command() export const add = new Command()
@@ -40,6 +41,11 @@ export const add = new Command()
.option("-a, --all", "add all available components", false) .option("-a, --all", "add all available components", false)
.option("-p, --path <path>", "the path to add the component to.") .option("-p, --path <path>", "the path to add the component to.")
.option("-s, --silent", "mute output.", false) .option("-s, --silent", "mute output.", false)
.option(
"--src-dir",
"use the src directory when creating a new project.",
false
)
.action(async (components, opts) => { .action(async (components, opts) => {
try { try {
const options = addOptionsSchema.parse({ const options = addOptionsSchema.parse({
@@ -100,6 +106,7 @@ export const add = new Command()
skipPreflight: false, skipPreflight: false,
silent: true, silent: true,
isNewProject: false, isNewProject: false,
srcDir: options.srcDir,
}) })
} }
@@ -108,6 +115,7 @@ export const add = new Command()
const { projectPath } = await createProject({ const { projectPath } = await createProject({
cwd: options.cwd, cwd: options.cwd,
force: options.overwrite, force: options.overwrite,
srcDir: options.srcDir,
}) })
if (!projectPath) { if (!projectPath) {
logger.break() logger.break()
@@ -123,6 +131,7 @@ export const add = new Command()
skipPreflight: true, skipPreflight: true,
silent: true, silent: true,
isNewProject: true, isNewProject: true,
srcDir: options.srcDir,
}) })
shouldUpdateAppIndex = shouldUpdateAppIndex =

View File

@@ -32,6 +32,7 @@ export const initOptionsSchema = z.object({
force: z.boolean(), force: z.boolean(),
silent: z.boolean(), silent: z.boolean(),
isNewProject: z.boolean(), isNewProject: z.boolean(),
srcDir: z.boolean().optional(),
}) })
export const init = new Command() export const init = new Command()
@@ -50,6 +51,11 @@ export const init = new Command()
process.cwd() process.cwd()
) )
.option("-s, --silent", "mute output.", false) .option("-s, --silent", "mute output.", false)
.option(
"--src-dir",
"use the src directory when creating a new project.",
false
)
.action(async (components, opts) => { .action(async (components, opts) => {
try { try {
const options = initOptionsSchema.parse({ const options = initOptionsSchema.parse({

View File

@@ -10,15 +10,20 @@ import prompts from "prompts"
import { z } from "zod" import { z } from "zod"
export async function createProject( export async function createProject(
options: Pick<z.infer<typeof initOptionsSchema>, "cwd" | "force"> options: Pick<z.infer<typeof initOptionsSchema>, "cwd" | "force" | "srcDir">
) { ) {
options = {
srcDir: false,
...options,
}
if (!options.force) { if (!options.force) {
const { proceed } = await prompts({ const { proceed } = await prompts({
type: "confirm", type: "confirm",
name: "proceed", name: "proceed",
message: `The path ${highlighter.info( message: `The path ${highlighter.info(
options.cwd options.cwd
)} is empty. Would you like to start a new ${highlighter.info( )} is does not contain a package.json file. Would you like to start a new ${highlighter.info(
"Next.js" "Next.js"
)} project?`, )} project?`,
initial: true, initial: true,
@@ -81,7 +86,7 @@ export async function createProject(
"--eslint", "--eslint",
"--typescript", "--typescript",
"--app", "--app",
"--no-src-dir", options.srcDir ? "--src-dir" : "--no-src-dir",
"--no-import-alias", "--no-import-alias",
`--use-${packageManager}`, `--use-${packageManager}`,
] ]
@@ -97,7 +102,7 @@ export async function createProject(
} catch (error) { } catch (error) {
logger.break() logger.break()
logger.error( logger.error(
`Something went wront creating a new Next.js project. Please try again.` `Something went wrong creating a new Next.js project. Please try again.`
) )
process.exit(1) process.exit(1)
} }

View File

@@ -164,7 +164,6 @@ async function fetchRegistry(paths: string[]) {
const results = await Promise.all( const results = await Promise.all(
paths.map(async (path) => { paths.map(async (path) => {
const url = getRegistryUrl(path) const url = getRegistryUrl(path)
console.log("👉", url)
const response = await fetch(url, { agent }) const response = await fetch(url, { agent })
if (!response.ok) { if (!response.ok) {
@@ -175,6 +174,31 @@ async function fetchRegistry(paths: string[]) {
404: "Not found", 404: "Not found",
500: "Internal server error", 500: "Internal server error",
} }
if (response.status === 401) {
throw new Error(
`You are not authorized to access the component at ${highlighter.info(
url
)}.\nIf this is a remote registry, you may need to authenticate.`
)
}
if (response.status === 404) {
throw new Error(
`The component at ${highlighter.info(
url
)} was not found.\nIt may not exist at the registry. Please make sure it is a valid component.`
)
}
if (response.status === 403) {
throw new Error(
`You do not have access to the component at ${highlighter.info(
url
)}.\nIf this is a remote registry, you may need to authenticate or a token.`
)
}
const result = await response.json() const result = await response.json()
const message = const message =
result && typeof result === "object" && "error" in result result && typeof result === "object" && "error" in result

View File

@@ -1,6 +1,7 @@
import { existsSync, promises as fs } from "fs" import { existsSync, promises as fs } from "fs"
import path, { basename } from "path" import path, { basename } from "path"
import { Config } from "@/src/utils/get-config" import { Config } from "@/src/utils/get-config"
import { getProjectInfo } from "@/src/utils/get-project-info"
import { highlighter } from "@/src/utils/highlighter" import { highlighter } from "@/src/utils/highlighter"
import { logger } from "@/src/utils/logger" import { logger } from "@/src/utils/logger"
import { import {
@@ -37,7 +38,12 @@ export async function updateFiles(
const filesCreatedSpinner = spinner(`Updating files.`, { const filesCreatedSpinner = spinner(`Updating files.`, {
silent: options.silent, silent: options.silent,
})?.start() })?.start()
const baseColor = await getRegistryBaseColor(config.tailwind.baseColor)
const [projectInfo, baseColor] = await Promise.all([
getProjectInfo(config.resolvedPaths.cwd),
getRegistryBaseColor(config.tailwind.baseColor),
])
const filesCreated = [] const filesCreated = []
const filesUpdated = [] const filesUpdated = []
const filesSkipped = [] const filesSkipped = []
@@ -52,7 +58,9 @@ export async function updateFiles(
let filePath = path.join(targetDir, fileName) let filePath = path.join(targetDir, fileName)
if (file.target) { if (file.target) {
filePath = path.join(config.resolvedPaths.cwd, file.target) filePath = projectInfo?.isSrcDir
? path.join(config.resolvedPaths.cwd, "src", file.target)
: path.join(config.resolvedPaths.cwd, file.target)
targetDir = path.dirname(filePath) targetDir = path.dirname(filePath)
} }

View File

@@ -196,12 +196,14 @@ async function addTailwindConfigTheme(
const themeObject = await parseObjectLiteral(themeObjectString) const themeObject = await parseObjectLiteral(themeObjectString)
const result = deepmerge(themeObject, theme) const result = deepmerge(themeObject, theme)
const resultString = objectToString(result) const resultString = objectToString(result)
.replace(/\'\"/g, "'") .replace(/\'\"/g, "'") // Replace `\" with "
.replace(/\"\'/g, "'") .replace(/\"\'/g, "'") // Replace `\" with "
.replace(/\'\[/g, "[") .replace(/\'\[/g, "[") // Replace `[ with [
.replace(/\]\'/g, "]") .replace(/\]\'/g, "]") // Replace `] with ]
.replace(/\\\'/g, "") .replace(/\'\\\'/g, "'") // Replace `\' with '
.replace(/\\\'/g, "") .replace(/\\\'/g, "'") // Replace \' with '
.replace(/\\\'\'/g, "'")
.replace(/\'\'/g, "'")
themeInitializer.replaceWithText(resultString) themeInitializer.replaceWithText(resultString)
} }

View File

@@ -12,11 +12,9 @@ exports[`registryResolveItemTree > should resolve index 1`] = `
"tailwindcss-animate", "tailwindcss-animate",
"class-variance-authority", "class-variance-authority",
"lucide-react", "lucide-react",
"",
"tailwindcss-animate", "tailwindcss-animate",
"class-variance-authority", "class-variance-authority",
"lucide-react", "lucide-react",
"",
"@radix-ui/react-label", "@radix-ui/react-label",
"clsx", "clsx",
"tailwind-merge", "tailwind-merge",

View File

@@ -369,6 +369,41 @@ export default config
" "
`; `;
exports[`transformTailwindConfig -> theme > should keep quotes in strings 1`] = `
"import type { Config } from 'tailwindcss'
const config: Config = {
darkMode: ["class"],
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
fontFamily: {
sans: ['Figtree', ...defaultTheme.fontFamily.sans]
},
colors: {
...defaultColors,
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))'
},
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))'
}
}
}
},
}
export default config
"
`;
exports[`transformTailwindConfig -> theme > should keep spread assignments 1`] = ` exports[`transformTailwindConfig -> theme > should keep spread assignments 1`] = `
"import type { Config } from 'tailwindcss' "import type { Config } from 'tailwindcss'

View File

@@ -760,6 +760,55 @@ export default config
expect(output3).toBe(output1) expect(output3).toBe(output1)
expect(output3).toBe(output2) expect(output3).toBe(output2)
}) })
test("should keep quotes in strings", async () => {
expect(
await transformTailwindConfig(
`import type { Config } from 'tailwindcss'
const config: Config = {
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
fontFamily: {
sans: ['Figtree', ...defaultTheme.fontFamily.sans],
},
colors: {
...defaultColors,
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
},
},
},
}
export default config
`,
{
theme: {
extend: {
colors: {
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
},
},
},
},
{
config: SHARED_CONFIG,
}
)
).toMatchSnapshot()
})
}) })
describe("nestSpreadProperties", () => { describe("nestSpreadProperties", () => {