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"],
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")),
source: "__registry__/new-york/block/sidebar-01.tsx",
source: "__registry__/new-york/block/sidebar-01/page.tsx",
category: "Application",
subcategory: "Dashboard",
chunks: []
@@ -2156,7 +2156,7 @@ export const Index: Record<string, any> = {
registryDependencies: ["button","card","input","label"],
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")),
source: "__registry__/new-york/block/login-01.tsx",
source: "__registry__/new-york/block/login-01/page.tsx",
category: "Authentication",
subcategory: "Login",
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"],
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")),
source: "__registry__/default/block/sidebar-01.tsx",
source: "__registry__/default/block/sidebar-01/page.tsx",
category: "Application",
subcategory: "Dashboard",
chunks: []
@@ -5512,7 +5512,7 @@ export const Index: Record<string, any> = {
registryDependencies: ["button","card","input","label"],
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")),
source: "__registry__/default/block/login-01.tsx",
source: "__registry__/default/block/login-01/page.tsx",
category: "Authentication",
subcategory: "Login",
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() {
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.

View File

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

View File

@@ -7,7 +7,7 @@ import { Separator } from "@/registry/new-york/ui/separator"
export function Announcement() {
return (
<Link
href="/docs/components/chart"
href="/docs/changelog"
className="group inline-flex items-center px-0.5 text-sm font-medium"
>
<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 mounted = useMounted()
if (!toc?.items || !mounted) {
if (!toc?.items?.length) {
return null
}

View File

@@ -77,6 +77,12 @@ export const docsConfig: DocsConfig = {
href: "/docs/components/typography",
items: [],
},
{
title: "Open in v0",
href: "/docs/v0",
items: [],
label: "New",
},
{
title: "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",
items: [

View File

@@ -5,7 +5,7 @@ description: Use the CLI to add components to your project.
<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>

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.
```tsx
import { useToast } from "@/components/ui/use-toast"
import { useToast } from "@/components/hooks/use-toast"
```
```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:
```txt showLineNumbers
- Where should we create your new project?
./your-app-name
- How would you like to start your new project?
Choose a starter template (or Empty)
- Install dependencies?
Yes
- Do you plan to write TypeScript?
Yes
- How strict should TypeScript be?
Strict
- Initialize a new git repository? (optional)
Yes/No
- Where should we create your new project? ./your-app-name
- How would you like to start your new project? Choose a template
- Do you plan to write TypeScript? Yes
- How strict should TypeScript be? Strict
- Install dependencies? Yes
- Initialize a new git repository? (optional) Yes/No
```
### 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
Install Tailwind CSS using the Astro CLI:
```bash
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
Add the following code to the `tsconfig.json` file to resolve paths:
```ts {4-9} showLineNumbers
```ts title="tsconfig.json" {4-9} showLineNumbers
{
"compilerOptions": {
// ...
@@ -81,62 +99,12 @@ Add the following code to the `tsconfig.json` file to resolve paths:
### Run the CLI
Run the `shadcn-ui` init command to setup your project:
Run the `shadcn` init command to setup your project:
```bash
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
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>
<p className="font-medium mt-2">Remix</p>
</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">
<svg
role="img"
@@ -86,6 +73,19 @@ description: How to install dependencies and structure your app.
</svg>
<p className="font-medium mt-2">Laravel</p>
</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">
<svg
role="img"

View File

@@ -15,7 +15,7 @@ laravel new my-app --typescript --breeze --stack=react --git --no-interaction
### Run the CLI
Run the `shadcn-ui` init command to setup your project:
Run the `shadcn` init command to setup your project:
```bash
npx shadcn@latest init
@@ -26,106 +26,9 @@ npx shadcn@latest init
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? 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")],
}
Which style would you like to use?
Which color would you like to use as base color?
Do you want to use CSS variables for colors? yes
```
### 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
```
<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
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
```
### 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
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": [
"tailwindcss-animate",
"class-variance-authority",
"lucide-react",
""
"lucide-react"
],
"registryDependencies": [
"utils"

View File

@@ -231,6 +231,18 @@ export const Index: Record<string, any> = {
// // Write the source file for blocks only.
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)
if (!existsSync(sourcePath)) {
await fs.mkdir(sourcePath, { recursive: true })
@@ -414,16 +426,21 @@ async function buildStylesIndex() {
for (const style of styles) {
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 = {
name: style.name,
type: "registry:style",
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" : "",
],
dependencies,
registryDependencies: ["utils"],
tailwind: {
config: {

View File

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

View File

@@ -1,5 +1,19 @@
# @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
### Major Changes

View File

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

View File

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

View File

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

View File

@@ -10,15 +10,20 @@ import prompts from "prompts"
import { z } from "zod"
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) {
const { proceed } = await prompts({
type: "confirm",
name: "proceed",
message: `The path ${highlighter.info(
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"
)} project?`,
initial: true,
@@ -81,7 +86,7 @@ export async function createProject(
"--eslint",
"--typescript",
"--app",
"--no-src-dir",
options.srcDir ? "--src-dir" : "--no-src-dir",
"--no-import-alias",
`--use-${packageManager}`,
]
@@ -97,7 +102,7 @@ export async function createProject(
} catch (error) {
logger.break()
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)
}

View File

@@ -164,7 +164,6 @@ async function fetchRegistry(paths: string[]) {
const results = await Promise.all(
paths.map(async (path) => {
const url = getRegistryUrl(path)
console.log("👉", url)
const response = await fetch(url, { agent })
if (!response.ok) {
@@ -175,6 +174,31 @@ async function fetchRegistry(paths: string[]) {
404: "Not found",
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 message =
result && typeof result === "object" && "error" in result

View File

@@ -1,6 +1,7 @@
import { existsSync, promises as fs } from "fs"
import path, { basename } from "path"
import { Config } from "@/src/utils/get-config"
import { getProjectInfo } from "@/src/utils/get-project-info"
import { highlighter } from "@/src/utils/highlighter"
import { logger } from "@/src/utils/logger"
import {
@@ -37,7 +38,12 @@ export async function updateFiles(
const filesCreatedSpinner = spinner(`Updating files.`, {
silent: options.silent,
})?.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 filesUpdated = []
const filesSkipped = []
@@ -52,7 +58,9 @@ export async function updateFiles(
let filePath = path.join(targetDir, fileName)
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)
}

View File

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

View File

@@ -12,11 +12,9 @@ exports[`registryResolveItemTree > should resolve index 1`] = `
"tailwindcss-animate",
"class-variance-authority",
"lucide-react",
"",
"tailwindcss-animate",
"class-variance-authority",
"lucide-react",
"",
"@radix-ui/react-label",
"clsx",
"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`] = `
"import type { Config } from 'tailwindcss'

View File

@@ -760,6 +760,55 @@ export default config
expect(output3).toBe(output1)
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", () => {