diff --git a/apps/v4/app/(view)/view/[style]/[name]/page.tsx b/apps/v4/app/(view)/view/[style]/[name]/page.tsx
index 77761346df..4bab7cad65 100644
--- a/apps/v4/app/(view)/view/[style]/[name]/page.tsx
+++ b/apps/v4/app/(view)/view/[style]/[name]/page.tsx
@@ -4,7 +4,11 @@ import { type Metadata } from "next"
import { notFound } from "next/navigation"
import { siteConfig } from "@/lib/config"
-import { getRegistryComponent, getRegistryItem } from "@/lib/registry"
+import {
+ getDemoItem,
+ getRegistryComponent,
+ getRegistryItem,
+} from "@/lib/registry"
import { absoluteUrl } from "@/lib/utils"
import { getStyle, legacyStyles, type Style } from "@/registry/_legacy-styles"
@@ -18,7 +22,12 @@ export const dynamicParams = false
const getCachedRegistryItem = React.cache(
async (name: string, styleName: Style["name"]) => {
- return await getRegistryItem(name, styleName)
+ // Try registry item first, then fallback to demo item (for examples).
+ const item = await getRegistryItem(name, styleName)
+ if (item) {
+ return item
+ }
+ return await getDemoItem(name, styleName)
}
)
@@ -75,9 +84,52 @@ export async function generateMetadata({
export async function generateStaticParams() {
const { Index } = await import("@/registry/__index__")
+ const { Index: BasesIndex } = await import("@/registry/bases/__index__")
+ const { ExamplesIndex } = await import("@/examples/__index__")
const params: Array<{ style: string; name: string }> = []
for (const style of legacyStyles) {
+ // Check if this is a base-prefixed style (e.g., base-nova, radix-nova).
+ const baseMatch = style.name.match(/^(base|radix)-/)
+ if (baseMatch) {
+ const baseName = baseMatch[1]
+
+ // Add examples from ExamplesIndex.
+ const examples = ExamplesIndex[baseName]
+ if (examples) {
+ for (const exampleName of Object.keys(examples)) {
+ params.push({
+ style: style.name,
+ name: exampleName,
+ })
+ }
+ }
+
+ // Add UI components from BasesIndex.
+ const baseIndex = BasesIndex[baseName]
+ if (baseIndex) {
+ for (const itemName in baseIndex) {
+ const item = baseIndex[itemName]
+ if (
+ [
+ "registry:block",
+ "registry:component",
+ "registry:example",
+ "registry:internal",
+ ].includes(item.type)
+ ) {
+ params.push({
+ style: style.name,
+ name: item.name,
+ })
+ }
+ }
+ }
+
+ continue
+ }
+
+ // Handle legacy styles (e.g., new-york-v4).
if (!Index[style.name]) {
continue
}
diff --git a/apps/v4/components/component-preview.tsx b/apps/v4/components/component-preview.tsx
index a983919ef1..693c5d1db9 100644
--- a/apps/v4/components/component-preview.tsx
+++ b/apps/v4/components/component-preview.tsx
@@ -29,14 +29,14 @@ export function ComponentPreview({
return (
-
+
A sidebar that collapses to icons.
@@ -318,15 +314,8 @@ export function AppSidebar() {
You should see something like this:
-
-
+
+
Your first sidebar.
@@ -545,14 +534,7 @@ Use the `SidebarHeader` component to add a sticky header to the sidebar.
The following example adds a `` to the `SidebarHeader`.
-
+
A sidebar header with a dropdown menu.
@@ -592,14 +574,7 @@ Use the `SidebarFooter` component to add a sticky footer to the sidebar.
The following example adds a `` to the `SidebarFooter`.
-
+
A sidebar footer with a dropdown menu.
@@ -672,14 +647,7 @@ Use the `SidebarGroup` component to create a section within the sidebar.
A `SidebarGroup` has a `SidebarGroupLabel`, a `SidebarGroupContent` and an optional `SidebarGroupAction`.
-
+
A sidebar group.
@@ -713,10 +681,7 @@ To make a `SidebarGroup` collapsible, wrap it in a `Collapsible`.
A collapsible sidebar group.
@@ -756,10 +721,7 @@ Use the `SidebarGroupAction` component to add an action button to the `SidebarGr
A sidebar group with an action button.
@@ -804,14 +766,7 @@ A `SidebarMenu` component is composed of `SidebarMenuItem`, `SidebarMenuButton`,
Here's an example of a `SidebarMenu` component rendering a list of projects.
-
+
A sidebar menu with a list of projects.
@@ -906,10 +861,7 @@ Here's an example of a `SidebarMenuAction` component rendering a `DropdownMenu`.
A sidebar menu action with a dropdown menu.
@@ -952,10 +904,7 @@ Use `` and `` to render a submenu
A sidebar menu with a submenu.
@@ -984,10 +933,7 @@ To make a `SidebarMenu` component collapsible, wrap it and the `SidebarMenuSub`
A collapsible sidebar menu.
@@ -1019,10 +965,7 @@ The `SidebarMenuBadge` component is used to render a badge within a `SidebarMenu
A sidebar menu with a badge.
@@ -1121,14 +1064,7 @@ The `SidebarRail` component is used to render a rail within a `Sidebar`. This ra
Here's an example of a `SidebarMenu` component rendering a list of projects using React Server Components.
-
+
A sidebar menu using React Server Components.
@@ -1274,10 +1210,7 @@ Use the `open` and `onOpenChange` props to control the sidebar.
A controlled sidebar.
diff --git a/apps/v4/content/docs/components/radix/sidebar.mdx b/apps/v4/content/docs/components/radix/sidebar.mdx
index 025db54271..a68b8e7c2e 100644
--- a/apps/v4/content/docs/components/radix/sidebar.mdx
+++ b/apps/v4/content/docs/components/radix/sidebar.mdx
@@ -321,7 +321,7 @@ export function AppSidebar() {
You should see something like this:
-
+
> = {
return { default: mod.default || mod[exportName] }
}),
},
+ "sidebar-controlled": {
+ name: "sidebar-controlled",
+ filePath: "examples/radix/sidebar-controlled.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./radix/sidebar-controlled")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-controlled"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
+ "sidebar-demo": {
+ name: "sidebar-demo",
+ filePath: "examples/radix/sidebar-demo.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./radix/sidebar-demo")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-demo"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
+ "sidebar-footer": {
+ name: "sidebar-footer",
+ filePath: "examples/radix/sidebar-footer.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./radix/sidebar-footer")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-footer"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
+ "sidebar-group-action": {
+ name: "sidebar-group-action",
+ filePath: "examples/radix/sidebar-group-action.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./radix/sidebar-group-action")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-group-action"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
+ "sidebar-group-collapsible": {
+ name: "sidebar-group-collapsible",
+ filePath: "examples/radix/sidebar-group-collapsible.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./radix/sidebar-group-collapsible")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-group-collapsible"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
+ "sidebar-group": {
+ name: "sidebar-group",
+ filePath: "examples/radix/sidebar-group.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./radix/sidebar-group")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-group"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
+ "sidebar-header": {
+ name: "sidebar-header",
+ filePath: "examples/radix/sidebar-header.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./radix/sidebar-header")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-header"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
+ "sidebar-menu-action": {
+ name: "sidebar-menu-action",
+ filePath: "examples/radix/sidebar-menu-action.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./radix/sidebar-menu-action")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-menu-action"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
+ "sidebar-menu-badge": {
+ name: "sidebar-menu-badge",
+ filePath: "examples/radix/sidebar-menu-badge.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./radix/sidebar-menu-badge")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-menu-badge"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
+ "sidebar-menu-collapsible": {
+ name: "sidebar-menu-collapsible",
+ filePath: "examples/radix/sidebar-menu-collapsible.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./radix/sidebar-menu-collapsible")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-menu-collapsible"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
+ "sidebar-menu-sub": {
+ name: "sidebar-menu-sub",
+ filePath: "examples/radix/sidebar-menu-sub.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./radix/sidebar-menu-sub")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-menu-sub"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
+ "sidebar-menu": {
+ name: "sidebar-menu",
+ filePath: "examples/radix/sidebar-menu.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./radix/sidebar-menu")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-menu"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
+ "sidebar-rsc": {
+ name: "sidebar-rsc",
+ filePath: "examples/radix/sidebar-rsc.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./radix/sidebar-rsc")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-rsc"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
"skeleton-avatar": {
name: "skeleton-avatar",
filePath: "examples/radix/skeleton-avatar.tsx",
@@ -8548,6 +8717,175 @@ export const ExamplesIndex: Record> = {
return { default: mod.default || mod[exportName] }
}),
},
+ "sidebar-controlled": {
+ name: "sidebar-controlled",
+ filePath: "examples/base/sidebar-controlled.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./base/sidebar-controlled")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-controlled"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
+ "sidebar-demo": {
+ name: "sidebar-demo",
+ filePath: "examples/base/sidebar-demo.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./base/sidebar-demo")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-demo"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
+ "sidebar-footer": {
+ name: "sidebar-footer",
+ filePath: "examples/base/sidebar-footer.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./base/sidebar-footer")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-footer"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
+ "sidebar-group-action": {
+ name: "sidebar-group-action",
+ filePath: "examples/base/sidebar-group-action.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./base/sidebar-group-action")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-group-action"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
+ "sidebar-group-collapsible": {
+ name: "sidebar-group-collapsible",
+ filePath: "examples/base/sidebar-group-collapsible.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./base/sidebar-group-collapsible")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-group-collapsible"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
+ "sidebar-group": {
+ name: "sidebar-group",
+ filePath: "examples/base/sidebar-group.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./base/sidebar-group")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-group"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
+ "sidebar-header": {
+ name: "sidebar-header",
+ filePath: "examples/base/sidebar-header.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./base/sidebar-header")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-header"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
+ "sidebar-menu-action": {
+ name: "sidebar-menu-action",
+ filePath: "examples/base/sidebar-menu-action.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./base/sidebar-menu-action")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-menu-action"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
+ "sidebar-menu-badge": {
+ name: "sidebar-menu-badge",
+ filePath: "examples/base/sidebar-menu-badge.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./base/sidebar-menu-badge")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-menu-badge"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
+ "sidebar-menu-collapsible": {
+ name: "sidebar-menu-collapsible",
+ filePath: "examples/base/sidebar-menu-collapsible.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./base/sidebar-menu-collapsible")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-menu-collapsible"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
+ "sidebar-menu-sub": {
+ name: "sidebar-menu-sub",
+ filePath: "examples/base/sidebar-menu-sub.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./base/sidebar-menu-sub")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-menu-sub"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
+ "sidebar-menu": {
+ name: "sidebar-menu",
+ filePath: "examples/base/sidebar-menu.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./base/sidebar-menu")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-menu"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
+ "sidebar-rsc": {
+ name: "sidebar-rsc",
+ filePath: "examples/base/sidebar-rsc.tsx",
+ component: React.lazy(async () => {
+ const mod = await import("./base/sidebar-rsc")
+ const exportName =
+ Object.keys(mod).find(
+ (key) =>
+ typeof mod[key] === "function" || typeof mod[key] === "object"
+ ) || "sidebar-rsc"
+ return { default: mod.default || mod[exportName] }
+ }),
+ },
"skeleton-avatar": {
name: "skeleton-avatar",
filePath: "examples/base/skeleton-avatar.tsx",
diff --git a/apps/v4/examples/base/sidebar-controlled.tsx b/apps/v4/examples/base/sidebar-controlled.tsx
new file mode 100644
index 0000000000..c472444c09
--- /dev/null
+++ b/apps/v4/examples/base/sidebar-controlled.tsx
@@ -0,0 +1,93 @@
+"use client"
+
+import * as React from "react"
+import { Button } from "@/examples/base/ui/button"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarInset,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+} from "@/examples/base/ui/sidebar"
+import {
+ FrameIcon,
+ LifeBuoyIcon,
+ MapIcon,
+ PanelLeftCloseIcon,
+ PanelLeftOpenIcon,
+ PieChartIcon,
+ SendIcon,
+} from "lucide-react"
+
+const projects = [
+ {
+ name: "Design Engineering",
+ url: "#",
+ icon: FrameIcon,
+ },
+ {
+ name: "Sales & Marketing",
+ url: "#",
+ icon: PieChartIcon,
+ },
+ {
+ name: "Travel",
+ url: "#",
+ icon: MapIcon,
+ },
+ {
+ name: "Support",
+ url: "#",
+ icon: LifeBuoyIcon,
+ },
+ {
+ name: "Feedback",
+ url: "#",
+ icon: SendIcon,
+ },
+]
+
+export default function AppSidebar() {
+ const [open, setOpen] = React.useState(true)
+
+ return (
+
+
+
+
+ Projects
+
+
+ {projects.map((project) => (
+
+ }>
+
+ {project.name}
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/v4/examples/base/sidebar-demo.tsx b/apps/v4/examples/base/sidebar-demo.tsx
new file mode 100644
index 0000000000..25db4a2a2b
--- /dev/null
+++ b/apps/v4/examples/base/sidebar-demo.tsx
@@ -0,0 +1,81 @@
+"use client"
+
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarInset,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/examples/base/ui/sidebar"
+import {
+ CalendarIcon,
+ HomeIcon,
+ InboxIcon,
+ SearchIcon,
+ SettingsIcon,
+} from "lucide-react"
+
+const items = [
+ {
+ title: "Home",
+ url: "#",
+ icon: HomeIcon,
+ },
+ {
+ title: "Inbox",
+ url: "#",
+ icon: InboxIcon,
+ },
+ {
+ title: "Calendar",
+ url: "#",
+ icon: CalendarIcon,
+ },
+ {
+ title: "Search",
+ url: "#",
+ icon: SearchIcon,
+ },
+ {
+ title: "Settings",
+ url: "#",
+ icon: SettingsIcon,
+ },
+]
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+ Application
+
+
+ {items.map((item) => (
+
+ }>
+
+ {item.title}
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/v4/examples/base/sidebar-footer.tsx b/apps/v4/examples/base/sidebar-footer.tsx
new file mode 100644
index 0000000000..003f6f980d
--- /dev/null
+++ b/apps/v4/examples/base/sidebar-footer.tsx
@@ -0,0 +1,67 @@
+"use client"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/examples/base/ui/dropdown-menu"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarFooter,
+ SidebarHeader,
+ SidebarInset,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/examples/base/ui/sidebar"
+import { ChevronUpIcon } from "lucide-react"
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+
+
+
+
+
+ }
+ >
+ Username
+
+
+
+
+ Account
+
+
+ Billing
+
+
+ Sign out
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/v4/examples/base/sidebar-group-action.tsx b/apps/v4/examples/base/sidebar-group-action.tsx
new file mode 100644
index 0000000000..050b66842a
--- /dev/null
+++ b/apps/v4/examples/base/sidebar-group-action.tsx
@@ -0,0 +1,64 @@
+"use client"
+
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupAction,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+} from "@/examples/base/ui/sidebar"
+import { FrameIcon, MapIcon, PieChartIcon, PlusIcon } from "lucide-react"
+import { toast, Toaster } from "sonner"
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+
+ Projects
+ toast("You clicked the group action!")}
+ >
+ Add Project
+
+
+
+
+ }>
+
+ Design Engineering
+
+
+
+ }>
+
+ Sales & Marketing
+
+
+
+ }>
+
+ Travel
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/v4/examples/base/sidebar-group-collapsible.tsx b/apps/v4/examples/base/sidebar-group-collapsible.tsx
new file mode 100644
index 0000000000..5fe16ad205
--- /dev/null
+++ b/apps/v4/examples/base/sidebar-group-collapsible.tsx
@@ -0,0 +1,59 @@
+"use client"
+
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/examples/base/ui/collapsible"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+} from "@/examples/base/ui/sidebar"
+import { ChevronDownIcon, LifeBuoyIcon, SendIcon } from "lucide-react"
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+
+ }
+ className="hover:bg-sidebar-accent hover:text-sidebar-accent-foreground text-sm"
+ >
+ Help
+
+
+
+
+
+
+
+
+ Support
+
+
+
+
+
+ Feedback
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/v4/examples/base/sidebar-group.tsx b/apps/v4/examples/base/sidebar-group.tsx
new file mode 100644
index 0000000000..497158a794
--- /dev/null
+++ b/apps/v4/examples/base/sidebar-group.tsx
@@ -0,0 +1,44 @@
+"use client"
+
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+} from "@/examples/base/ui/sidebar"
+import { LifeBuoyIcon, SendIcon } from "lucide-react"
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+ Help
+
+
+
+
+
+ Support
+
+
+
+
+
+ Feedback
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/v4/examples/base/sidebar-header.tsx b/apps/v4/examples/base/sidebar-header.tsx
new file mode 100644
index 0000000000..2e5c4bd142
--- /dev/null
+++ b/apps/v4/examples/base/sidebar-header.tsx
@@ -0,0 +1,57 @@
+"use client"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/examples/base/ui/dropdown-menu"
+import {
+ Sidebar,
+ SidebarHeader,
+ SidebarInset,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/examples/base/ui/sidebar"
+import { ChevronDownIcon } from "lucide-react"
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+
+
+
+ }
+ >
+ Select Workspace
+
+
+
+
+ Acme Inc
+
+
+ Acme Corp.
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/v4/examples/base/sidebar-menu-action.tsx b/apps/v4/examples/base/sidebar-menu-action.tsx
new file mode 100644
index 0000000000..77009ef61e
--- /dev/null
+++ b/apps/v4/examples/base/sidebar-menu-action.tsx
@@ -0,0 +1,99 @@
+"use client"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/examples/base/ui/dropdown-menu"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuAction,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+} from "@/examples/base/ui/sidebar"
+import {
+ FrameIcon,
+ LifeBuoyIcon,
+ MapIcon,
+ MoreHorizontalIcon,
+ PieChartIcon,
+ SendIcon,
+} from "lucide-react"
+
+const projects = [
+ {
+ name: "Design Engineering",
+ url: "#",
+ icon: FrameIcon,
+ },
+ {
+ name: "Sales & Marketing",
+ url: "#",
+ icon: PieChartIcon,
+ },
+ {
+ name: "Travel",
+ url: "#",
+ icon: MapIcon,
+ },
+ {
+ name: "Support",
+ url: "#",
+ icon: LifeBuoyIcon,
+ },
+ {
+ name: "Feedback",
+ url: "#",
+ icon: SendIcon,
+ },
+]
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+ Projects
+
+
+ {projects.map((project) => (
+
+ }
+ className="group-has-[[data-state=open]]/menu-item:bg-sidebar-accent"
+ >
+
+ {project.name}
+
+
+ }>
+
+ More
+
+
+
+ Edit Project
+
+
+ Delete Project
+
+
+
+
+ ))}
+
+
+
+
+
+
+ )
+}
diff --git a/apps/v4/examples/base/sidebar-menu-badge.tsx b/apps/v4/examples/base/sidebar-menu-badge.tsx
new file mode 100644
index 0000000000..54d4cd1451
--- /dev/null
+++ b/apps/v4/examples/base/sidebar-menu-badge.tsx
@@ -0,0 +1,84 @@
+"use client"
+
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuBadge,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+} from "@/examples/base/ui/sidebar"
+import {
+ FrameIcon,
+ LifeBuoyIcon,
+ MapIcon,
+ PieChartIcon,
+ SendIcon,
+} from "lucide-react"
+
+const projects = [
+ {
+ name: "Design Engineering",
+ url: "#",
+ icon: FrameIcon,
+ badge: "24",
+ },
+ {
+ name: "Sales & Marketing",
+ url: "#",
+ icon: PieChartIcon,
+ badge: "12",
+ },
+ {
+ name: "Travel",
+ url: "#",
+ icon: MapIcon,
+ badge: "3",
+ },
+ {
+ name: "Support",
+ url: "#",
+ icon: LifeBuoyIcon,
+ badge: "21",
+ },
+ {
+ name: "Feedback",
+ url: "#",
+ icon: SendIcon,
+ badge: "8",
+ },
+]
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+ Projects
+
+
+ {projects.map((project) => (
+
+ }
+ className="group-has-[[data-state=open]]/menu-item:bg-sidebar-accent"
+ >
+
+ {project.name}
+
+ {project.badge}
+
+ ))}
+
+
+
+
+
+
+ )
+}
diff --git a/apps/v4/examples/base/sidebar-menu-collapsible.tsx b/apps/v4/examples/base/sidebar-menu-collapsible.tsx
new file mode 100644
index 0000000000..b404baf747
--- /dev/null
+++ b/apps/v4/examples/base/sidebar-menu-collapsible.tsx
@@ -0,0 +1,193 @@
+"use client"
+
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/examples/base/ui/collapsible"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarMenuSub,
+ SidebarMenuSubButton,
+ SidebarMenuSubItem,
+ SidebarProvider,
+} from "@/examples/base/ui/sidebar"
+import { ChevronRightIcon } from "lucide-react"
+
+const items = [
+ {
+ title: "Getting Started",
+ url: "#",
+ items: [
+ {
+ title: "Installation",
+ url: "#",
+ },
+ {
+ title: "Project Structure",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Building Your Application",
+ url: "#",
+ items: [
+ {
+ title: "Routing",
+ url: "#",
+ },
+ {
+ title: "Data Fetching",
+ url: "#",
+ isActive: true,
+ },
+ {
+ title: "Rendering",
+ url: "#",
+ },
+ {
+ title: "Caching",
+ url: "#",
+ },
+ {
+ title: "Styling",
+ url: "#",
+ },
+ {
+ title: "Optimizing",
+ url: "#",
+ },
+ {
+ title: "Configuring",
+ url: "#",
+ },
+ {
+ title: "Testing",
+ url: "#",
+ },
+ {
+ title: "Authentication",
+ url: "#",
+ },
+ {
+ title: "Deploying",
+ url: "#",
+ },
+ {
+ title: "Upgrading",
+ url: "#",
+ },
+ {
+ title: "Examples",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "API Reference",
+ url: "#",
+ items: [
+ {
+ title: "Components",
+ url: "#",
+ },
+ {
+ title: "File Conventions",
+ url: "#",
+ },
+ {
+ title: "Functions",
+ url: "#",
+ },
+ {
+ title: "next.config.js Options",
+ url: "#",
+ },
+ {
+ title: "CLI",
+ url: "#",
+ },
+ {
+ title: "Edge Runtime",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Architecture",
+ url: "#",
+ items: [
+ {
+ title: "Accessibility",
+ url: "#",
+ },
+ {
+ title: "Fast Refresh",
+ url: "#",
+ },
+ {
+ title: "Next.js Compiler",
+ url: "#",
+ },
+ {
+ title: "Supported Browsers",
+ url: "#",
+ },
+ {
+ title: "Turbopack",
+ url: "#",
+ },
+ ],
+ },
+]
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+
+
+ {items.map((item, index) => (
+
+
+ }>
+ {item.title}
+
+
+
+
+ {item.items.map((subItem, subIndex) => (
+
+ }
+ >
+ {subItem.title}
+
+
+ ))}
+
+
+
+
+ ))}
+
+
+
+
+
+
+ )
+}
diff --git a/apps/v4/examples/base/sidebar-menu-sub.tsx b/apps/v4/examples/base/sidebar-menu-sub.tsx
new file mode 100644
index 0000000000..b39a1e3dbe
--- /dev/null
+++ b/apps/v4/examples/base/sidebar-menu-sub.tsx
@@ -0,0 +1,178 @@
+"use client"
+
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarMenuSub,
+ SidebarMenuSubButton,
+ SidebarMenuSubItem,
+ SidebarProvider,
+} from "@/examples/base/ui/sidebar"
+
+const items = [
+ {
+ title: "Getting Started",
+ url: "#",
+ items: [
+ {
+ title: "Installation",
+ url: "#",
+ },
+ {
+ title: "Project Structure",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Building Your Application",
+ url: "#",
+ items: [
+ {
+ title: "Routing",
+ url: "#",
+ },
+ {
+ title: "Data Fetching",
+ url: "#",
+ isActive: true,
+ },
+ {
+ title: "Rendering",
+ url: "#",
+ },
+ {
+ title: "Caching",
+ url: "#",
+ },
+ {
+ title: "Styling",
+ url: "#",
+ },
+ {
+ title: "Optimizing",
+ url: "#",
+ },
+ {
+ title: "Configuring",
+ url: "#",
+ },
+ {
+ title: "Testing",
+ url: "#",
+ },
+ {
+ title: "Authentication",
+ url: "#",
+ },
+ {
+ title: "Deploying",
+ url: "#",
+ },
+ {
+ title: "Upgrading",
+ url: "#",
+ },
+ {
+ title: "Examples",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "API Reference",
+ url: "#",
+ items: [
+ {
+ title: "Components",
+ url: "#",
+ },
+ {
+ title: "File Conventions",
+ url: "#",
+ },
+ {
+ title: "Functions",
+ url: "#",
+ },
+ {
+ title: "next.config.js Options",
+ url: "#",
+ },
+ {
+ title: "CLI",
+ url: "#",
+ },
+ {
+ title: "Edge Runtime",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Architecture",
+ url: "#",
+ items: [
+ {
+ title: "Accessibility",
+ url: "#",
+ },
+ {
+ title: "Fast Refresh",
+ url: "#",
+ },
+ {
+ title: "Next.js Compiler",
+ url: "#",
+ },
+ {
+ title: "Supported Browsers",
+ url: "#",
+ },
+ {
+ title: "Turbopack",
+ url: "#",
+ },
+ ],
+ },
+]
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+
+
+ {items.map((item, index) => (
+
+ }>
+ {item.title}
+
+
+ {item.items.map((subItem, subIndex) => (
+
+ }
+ >
+ {subItem.title}
+
+
+ ))}
+
+
+ ))}
+
+
+
+
+
+
+ )
+}
diff --git a/apps/v4/examples/base/sidebar-menu.tsx b/apps/v4/examples/base/sidebar-menu.tsx
new file mode 100644
index 0000000000..dc8b0362aa
--- /dev/null
+++ b/apps/v4/examples/base/sidebar-menu.tsx
@@ -0,0 +1,74 @@
+"use client"
+
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+} from "@/examples/base/ui/sidebar"
+import {
+ FrameIcon,
+ LifeBuoyIcon,
+ MapIcon,
+ PieChartIcon,
+ SendIcon,
+} from "lucide-react"
+
+const projects = [
+ {
+ name: "Design Engineering",
+ url: "#",
+ icon: FrameIcon,
+ },
+ {
+ name: "Sales & Marketing",
+ url: "#",
+ icon: PieChartIcon,
+ },
+ {
+ name: "Travel",
+ url: "#",
+ icon: MapIcon,
+ },
+ {
+ name: "Support",
+ url: "#",
+ icon: LifeBuoyIcon,
+ },
+ {
+ name: "Feedback",
+ url: "#",
+ icon: SendIcon,
+ },
+]
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+ Projects
+
+
+ {projects.map((project) => (
+
+ }>
+
+ {project.name}
+
+
+ ))}
+
+
+
+
+
+
+ )
+}
diff --git a/apps/v4/examples/base/sidebar-rsc.tsx b/apps/v4/examples/base/sidebar-rsc.tsx
new file mode 100644
index 0000000000..f3e6029dd3
--- /dev/null
+++ b/apps/v4/examples/base/sidebar-rsc.tsx
@@ -0,0 +1,107 @@
+import * as React from "react"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarMenuSkeleton,
+ SidebarProvider,
+} from "@/examples/base/ui/sidebar"
+import {
+ FrameIcon,
+ LifeBuoyIcon,
+ MapIcon,
+ PieChartIcon,
+ SendIcon,
+} from "lucide-react"
+
+const projects = [
+ {
+ name: "Design Engineering",
+ url: "#",
+ icon: FrameIcon,
+ badge: "24",
+ },
+ {
+ name: "Sales & Marketing",
+ url: "#",
+ icon: PieChartIcon,
+ badge: "12",
+ },
+ {
+ name: "Travel",
+ url: "#",
+ icon: MapIcon,
+ badge: "3",
+ },
+ {
+ name: "Support",
+ url: "#",
+ icon: LifeBuoyIcon,
+ badge: "21",
+ },
+ {
+ name: "Feedback",
+ url: "#",
+ icon: SendIcon,
+ badge: "8",
+ },
+]
+
+// Dummy fetch function.
+async function fetchProjects() {
+ await new Promise((resolve) => setTimeout(resolve, 3000))
+ return projects
+}
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+ Projects
+
+ }>
+
+
+
+
+
+
+
+ )
+}
+
+function NavProjectsSkeleton() {
+ return (
+
+ {Array.from({ length: 5 }).map((_, index) => (
+
+
+
+ ))}
+
+ )
+}
+
+async function NavProjects() {
+ const projects = await fetchProjects()
+
+ return (
+
+ {projects.map((project) => (
+
+ }>
+
+ {project.name}
+
+
+ ))}
+
+ )
+}
diff --git a/apps/v4/examples/base/ui/navigation-menu.tsx b/apps/v4/examples/base/ui/navigation-menu.tsx
index ff81723570..28254365e2 100644
--- a/apps/v4/examples/base/ui/navigation-menu.tsx
+++ b/apps/v4/examples/base/ui/navigation-menu.tsx
@@ -27,7 +27,7 @@ function NavigationMenu({
function NavigationMenuList({
className,
...props
-}: NavigationMenuPrimitive.List.Props) {
+}: React.ComponentPropsWithRef) {
return (
) {
return (
) {
return (
+
+
+
+ Projects
+
+
+ {projects.map((project) => (
+
+
+
+
+ {project.name}
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/v4/examples/radix/sidebar-demo.tsx b/apps/v4/examples/radix/sidebar-demo.tsx
new file mode 100644
index 0000000000..79b16a835f
--- /dev/null
+++ b/apps/v4/examples/radix/sidebar-demo.tsx
@@ -0,0 +1,83 @@
+"use client"
+
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarInset,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/examples/radix/ui/sidebar"
+import {
+ CalendarIcon,
+ HomeIcon,
+ InboxIcon,
+ SearchIcon,
+ SettingsIcon,
+} from "lucide-react"
+
+const items = [
+ {
+ title: "Home",
+ url: "#",
+ icon: HomeIcon,
+ },
+ {
+ title: "Inbox",
+ url: "#",
+ icon: InboxIcon,
+ },
+ {
+ title: "Calendar",
+ url: "#",
+ icon: CalendarIcon,
+ },
+ {
+ title: "Search",
+ url: "#",
+ icon: SearchIcon,
+ },
+ {
+ title: "Settings",
+ url: "#",
+ icon: SettingsIcon,
+ },
+]
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+ Application
+
+
+ {items.map((item) => (
+
+
+
+
+ {item.title}
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/v4/examples/radix/sidebar-footer.tsx b/apps/v4/examples/radix/sidebar-footer.tsx
new file mode 100644
index 0000000000..1b4803d017
--- /dev/null
+++ b/apps/v4/examples/radix/sidebar-footer.tsx
@@ -0,0 +1,65 @@
+"use client"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/examples/radix/ui/dropdown-menu"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarFooter,
+ SidebarHeader,
+ SidebarInset,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/examples/radix/ui/sidebar"
+import { ChevronUpIcon } from "lucide-react"
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+
+
+
+
+
+
+ Username
+
+
+
+
+
+ Account
+
+
+ Billing
+
+
+ Sign out
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/v4/examples/radix/sidebar-group-action.tsx b/apps/v4/examples/radix/sidebar-group-action.tsx
new file mode 100644
index 0000000000..542435b0f0
--- /dev/null
+++ b/apps/v4/examples/radix/sidebar-group-action.tsx
@@ -0,0 +1,70 @@
+"use client"
+
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupAction,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+} from "@/examples/radix/ui/sidebar"
+import { FrameIcon, MapIcon, PieChartIcon, PlusIcon } from "lucide-react"
+import { toast, Toaster } from "sonner"
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+
+ Projects
+ toast("You clicked the group action!")}
+ >
+ Add Project
+
+
+
+
+
+
+
+ Design Engineering
+
+
+
+
+
+
+
+ Sales & Marketing
+
+
+
+
+
+
+
+ Travel
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/v4/examples/radix/sidebar-group-collapsible.tsx b/apps/v4/examples/radix/sidebar-group-collapsible.tsx
new file mode 100644
index 0000000000..419d19c64a
--- /dev/null
+++ b/apps/v4/examples/radix/sidebar-group-collapsible.tsx
@@ -0,0 +1,61 @@
+"use client"
+
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/examples/radix/ui/collapsible"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+} from "@/examples/radix/ui/sidebar"
+import { ChevronDownIcon, LifeBuoyIcon, SendIcon } from "lucide-react"
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+
+
+
+ Help
+
+
+
+
+
+
+
+
+
+ Support
+
+
+
+
+
+ Feedback
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/v4/examples/radix/sidebar-group.tsx b/apps/v4/examples/radix/sidebar-group.tsx
new file mode 100644
index 0000000000..8e51c15392
--- /dev/null
+++ b/apps/v4/examples/radix/sidebar-group.tsx
@@ -0,0 +1,44 @@
+"use client"
+
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+} from "@/examples/radix/ui/sidebar"
+import { LifeBuoyIcon, SendIcon } from "lucide-react"
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+ Help
+
+
+
+
+
+ Support
+
+
+
+
+
+ Feedback
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/v4/examples/radix/sidebar-header.tsx b/apps/v4/examples/radix/sidebar-header.tsx
new file mode 100644
index 0000000000..5cd9bc4563
--- /dev/null
+++ b/apps/v4/examples/radix/sidebar-header.tsx
@@ -0,0 +1,55 @@
+"use client"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/examples/radix/ui/dropdown-menu"
+import {
+ Sidebar,
+ SidebarHeader,
+ SidebarInset,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/examples/radix/ui/sidebar"
+import { ChevronDownIcon } from "lucide-react"
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+
+
+
+
+ Select Workspace
+
+
+
+
+
+ Acme Inc
+
+
+ Acme Corp.
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/v4/examples/radix/sidebar-menu-action.tsx b/apps/v4/examples/radix/sidebar-menu-action.tsx
new file mode 100644
index 0000000000..602b847c82
--- /dev/null
+++ b/apps/v4/examples/radix/sidebar-menu-action.tsx
@@ -0,0 +1,103 @@
+"use client"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/examples/radix/ui/dropdown-menu"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuAction,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+} from "@/examples/radix/ui/sidebar"
+import {
+ FrameIcon,
+ LifeBuoyIcon,
+ MapIcon,
+ MoreHorizontalIcon,
+ PieChartIcon,
+ SendIcon,
+} from "lucide-react"
+
+const projects = [
+ {
+ name: "Design Engineering",
+ url: "#",
+ icon: FrameIcon,
+ },
+ {
+ name: "Sales & Marketing",
+ url: "#",
+ icon: PieChartIcon,
+ },
+ {
+ name: "Travel",
+ url: "#",
+ icon: MapIcon,
+ },
+ {
+ name: "Support",
+ url: "#",
+ icon: LifeBuoyIcon,
+ },
+ {
+ name: "Feedback",
+ url: "#",
+ icon: SendIcon,
+ },
+]
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+ Projects
+
+
+ {projects.map((project) => (
+
+
+
+
+ {project.name}
+
+
+
+
+
+
+ More
+
+
+
+
+ Edit Project
+
+
+ Delete Project
+
+
+
+
+ ))}
+
+
+
+
+
+
+ )
+}
diff --git a/apps/v4/examples/radix/sidebar-menu-badge.tsx b/apps/v4/examples/radix/sidebar-menu-badge.tsx
new file mode 100644
index 0000000000..375c016fd4
--- /dev/null
+++ b/apps/v4/examples/radix/sidebar-menu-badge.tsx
@@ -0,0 +1,86 @@
+"use client"
+
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuBadge,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+} from "@/examples/radix/ui/sidebar"
+import {
+ FrameIcon,
+ LifeBuoyIcon,
+ MapIcon,
+ PieChartIcon,
+ SendIcon,
+} from "lucide-react"
+
+const projects = [
+ {
+ name: "Design Engineering",
+ url: "#",
+ icon: FrameIcon,
+ badge: "24",
+ },
+ {
+ name: "Sales & Marketing",
+ url: "#",
+ icon: PieChartIcon,
+ badge: "12",
+ },
+ {
+ name: "Travel",
+ url: "#",
+ icon: MapIcon,
+ badge: "3",
+ },
+ {
+ name: "Support",
+ url: "#",
+ icon: LifeBuoyIcon,
+ badge: "21",
+ },
+ {
+ name: "Feedback",
+ url: "#",
+ icon: SendIcon,
+ badge: "8",
+ },
+]
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+ Projects
+
+
+ {projects.map((project) => (
+
+
+
+
+ {project.name}
+
+
+ {project.badge}
+
+ ))}
+
+
+
+
+
+
+ )
+}
diff --git a/apps/v4/examples/radix/sidebar-menu-collapsible.tsx b/apps/v4/examples/radix/sidebar-menu-collapsible.tsx
new file mode 100644
index 0000000000..c23c01db51
--- /dev/null
+++ b/apps/v4/examples/radix/sidebar-menu-collapsible.tsx
@@ -0,0 +1,195 @@
+"use client"
+
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/examples/radix/ui/collapsible"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarMenuSub,
+ SidebarMenuSubButton,
+ SidebarMenuSubItem,
+ SidebarProvider,
+} from "@/examples/radix/ui/sidebar"
+import { ChevronRightIcon } from "lucide-react"
+
+const items = [
+ {
+ title: "Getting Started",
+ url: "#",
+ items: [
+ {
+ title: "Installation",
+ url: "#",
+ },
+ {
+ title: "Project Structure",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Building Your Application",
+ url: "#",
+ items: [
+ {
+ title: "Routing",
+ url: "#",
+ },
+ {
+ title: "Data Fetching",
+ url: "#",
+ isActive: true,
+ },
+ {
+ title: "Rendering",
+ url: "#",
+ },
+ {
+ title: "Caching",
+ url: "#",
+ },
+ {
+ title: "Styling",
+ url: "#",
+ },
+ {
+ title: "Optimizing",
+ url: "#",
+ },
+ {
+ title: "Configuring",
+ url: "#",
+ },
+ {
+ title: "Testing",
+ url: "#",
+ },
+ {
+ title: "Authentication",
+ url: "#",
+ },
+ {
+ title: "Deploying",
+ url: "#",
+ },
+ {
+ title: "Upgrading",
+ url: "#",
+ },
+ {
+ title: "Examples",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "API Reference",
+ url: "#",
+ items: [
+ {
+ title: "Components",
+ url: "#",
+ },
+ {
+ title: "File Conventions",
+ url: "#",
+ },
+ {
+ title: "Functions",
+ url: "#",
+ },
+ {
+ title: "next.config.js Options",
+ url: "#",
+ },
+ {
+ title: "CLI",
+ url: "#",
+ },
+ {
+ title: "Edge Runtime",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Architecture",
+ url: "#",
+ items: [
+ {
+ title: "Accessibility",
+ url: "#",
+ },
+ {
+ title: "Fast Refresh",
+ url: "#",
+ },
+ {
+ title: "Next.js Compiler",
+ url: "#",
+ },
+ {
+ title: "Supported Browsers",
+ url: "#",
+ },
+ {
+ title: "Turbopack",
+ url: "#",
+ },
+ ],
+ },
+]
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+
+
+ {items.map((item, index) => (
+
+
+
+
+ {item.title}
+
+
+
+
+
+ {item.items.map((subItem, subIndex) => (
+
+
+
+ {subItem.title}
+
+
+
+ ))}
+
+
+
+
+ ))}
+
+
+
+
+
+
+ )
+}
diff --git a/apps/v4/examples/radix/sidebar-menu-sub.tsx b/apps/v4/examples/radix/sidebar-menu-sub.tsx
new file mode 100644
index 0000000000..2746f9aa8d
--- /dev/null
+++ b/apps/v4/examples/radix/sidebar-menu-sub.tsx
@@ -0,0 +1,180 @@
+"use client"
+
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarMenuSub,
+ SidebarMenuSubButton,
+ SidebarMenuSubItem,
+ SidebarProvider,
+} from "@/examples/radix/ui/sidebar"
+
+const items = [
+ {
+ title: "Getting Started",
+ url: "#",
+ items: [
+ {
+ title: "Installation",
+ url: "#",
+ },
+ {
+ title: "Project Structure",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Building Your Application",
+ url: "#",
+ items: [
+ {
+ title: "Routing",
+ url: "#",
+ },
+ {
+ title: "Data Fetching",
+ url: "#",
+ isActive: true,
+ },
+ {
+ title: "Rendering",
+ url: "#",
+ },
+ {
+ title: "Caching",
+ url: "#",
+ },
+ {
+ title: "Styling",
+ url: "#",
+ },
+ {
+ title: "Optimizing",
+ url: "#",
+ },
+ {
+ title: "Configuring",
+ url: "#",
+ },
+ {
+ title: "Testing",
+ url: "#",
+ },
+ {
+ title: "Authentication",
+ url: "#",
+ },
+ {
+ title: "Deploying",
+ url: "#",
+ },
+ {
+ title: "Upgrading",
+ url: "#",
+ },
+ {
+ title: "Examples",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "API Reference",
+ url: "#",
+ items: [
+ {
+ title: "Components",
+ url: "#",
+ },
+ {
+ title: "File Conventions",
+ url: "#",
+ },
+ {
+ title: "Functions",
+ url: "#",
+ },
+ {
+ title: "next.config.js Options",
+ url: "#",
+ },
+ {
+ title: "CLI",
+ url: "#",
+ },
+ {
+ title: "Edge Runtime",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Architecture",
+ url: "#",
+ items: [
+ {
+ title: "Accessibility",
+ url: "#",
+ },
+ {
+ title: "Fast Refresh",
+ url: "#",
+ },
+ {
+ title: "Next.js Compiler",
+ url: "#",
+ },
+ {
+ title: "Supported Browsers",
+ url: "#",
+ },
+ {
+ title: "Turbopack",
+ url: "#",
+ },
+ ],
+ },
+]
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+
+
+ {items.map((item, index) => (
+
+
+
+ {item.title}
+
+
+
+ {item.items.map((subItem, subIndex) => (
+
+
+
+ {subItem.title}
+
+
+
+ ))}
+
+
+ ))}
+
+
+
+
+
+
+ )
+}
diff --git a/apps/v4/examples/radix/sidebar-menu.tsx b/apps/v4/examples/radix/sidebar-menu.tsx
new file mode 100644
index 0000000000..124b9827b0
--- /dev/null
+++ b/apps/v4/examples/radix/sidebar-menu.tsx
@@ -0,0 +1,76 @@
+"use client"
+
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+} from "@/examples/radix/ui/sidebar"
+import {
+ FrameIcon,
+ LifeBuoyIcon,
+ MapIcon,
+ PieChartIcon,
+ SendIcon,
+} from "lucide-react"
+
+const projects = [
+ {
+ name: "Design Engineering",
+ url: "#",
+ icon: FrameIcon,
+ },
+ {
+ name: "Sales & Marketing",
+ url: "#",
+ icon: PieChartIcon,
+ },
+ {
+ name: "Travel",
+ url: "#",
+ icon: MapIcon,
+ },
+ {
+ name: "Support",
+ url: "#",
+ icon: LifeBuoyIcon,
+ },
+ {
+ name: "Feedback",
+ url: "#",
+ icon: SendIcon,
+ },
+]
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+ Projects
+
+
+ {projects.map((project) => (
+
+
+
+
+ {project.name}
+
+
+
+ ))}
+
+
+
+
+
+
+ )
+}
diff --git a/apps/v4/examples/radix/sidebar-rsc.tsx b/apps/v4/examples/radix/sidebar-rsc.tsx
new file mode 100644
index 0000000000..77f3f6beba
--- /dev/null
+++ b/apps/v4/examples/radix/sidebar-rsc.tsx
@@ -0,0 +1,109 @@
+import * as React from "react"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarMenuSkeleton,
+ SidebarProvider,
+} from "@/examples/radix/ui/sidebar"
+import {
+ FrameIcon,
+ LifeBuoyIcon,
+ MapIcon,
+ PieChartIcon,
+ SendIcon,
+} from "lucide-react"
+
+const projects = [
+ {
+ name: "Design Engineering",
+ url: "#",
+ icon: FrameIcon,
+ badge: "24",
+ },
+ {
+ name: "Sales & Marketing",
+ url: "#",
+ icon: PieChartIcon,
+ badge: "12",
+ },
+ {
+ name: "Travel",
+ url: "#",
+ icon: MapIcon,
+ badge: "3",
+ },
+ {
+ name: "Support",
+ url: "#",
+ icon: LifeBuoyIcon,
+ badge: "21",
+ },
+ {
+ name: "Feedback",
+ url: "#",
+ icon: SendIcon,
+ badge: "8",
+ },
+]
+
+// Dummy fetch function.
+async function fetchProjects() {
+ await new Promise((resolve) => setTimeout(resolve, 3000))
+ return projects
+}
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+ Projects
+
+ }>
+
+
+
+
+
+
+
+ )
+}
+
+function NavProjectsSkeleton() {
+ return (
+
+ {Array.from({ length: 5 }).map((_, index) => (
+
+
+
+ ))}
+
+ )
+}
+
+async function NavProjects() {
+ const projects = await fetchProjects()
+
+ return (
+
+ {projects.map((project) => (
+
+
+
+
+ {project.name}
+
+
+
+ ))}
+
+ )
+}
diff --git a/apps/v4/registry/_legacy-styles.ts b/apps/v4/registry/_legacy-styles.ts
index c53d31b3b1..e58353db4b 100644
--- a/apps/v4/registry/_legacy-styles.ts
+++ b/apps/v4/registry/_legacy-styles.ts
@@ -3,6 +3,14 @@ export const legacyStyles = [
name: "new-york-v4",
title: "New York",
},
+ {
+ name: "base-nova",
+ title: "Base Nova",
+ },
+ {
+ name: "radix-nova",
+ title: "Radix Nova",
+ },
] as const
export type Style = (typeof legacyStyles)[number]
diff --git a/apps/v4/registry/bases/base/ui/navigation-menu.tsx b/apps/v4/registry/bases/base/ui/navigation-menu.tsx
index 5b1e350159..d8f66280af 100644
--- a/apps/v4/registry/bases/base/ui/navigation-menu.tsx
+++ b/apps/v4/registry/bases/base/ui/navigation-menu.tsx
@@ -27,7 +27,7 @@ function NavigationMenu({
function NavigationMenuList({
className,
...props
-}: NavigationMenuPrimitive.List.Props) {
+}: React.ComponentPropsWithRef) {
return (
) {
return (
) {
return (