
3D Extruded Buildings & Urban Footprints
Real-world 3D building extrusions with dynamic sunlight shadows, terrain DEM elevations, and pitch/bearing camera controls.
Docs-style sidebar with a version switcher at the top, a search input below, and collapsible navigation groups. All sample data inlined so consumers edit routes in place.
Also available for Vue ->$pnpm dlx shadcn@latest add https://uipkge.dev/r/react/sidebar-03.json$npx shadcn@latest add https://uipkge.dev/r/react/sidebar-03.json$yarn dlx shadcn@latest add https://uipkge.dev/r/react/sidebar-03.json$bunx shadcn@latest add https://uipkge.dev/r/react/sidebar-03.jsonnpx shadcn@latest add @uipkge-react/sidebar-03Installs to:components/blocks/sidebar-03/'use client'
import { ChevronRight } from 'lucide-react'
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'
import {
Sidebar,
SidebarContent,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarRail,
} from '@/components/ui/sidebar'
import { SearchForm } from './SearchForm'
import { VersionSwitcher } from './VersionSwitcher'
// This is sample data.
const data = {
versions: ['1.0.1', '1.1.0-alpha', '2.0.0-beta1'],
navMain: [
{
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: '#' },
],
},
{
title: 'Community',
url: '#',
items: [{ title: 'Contribution Guide', url: '#' }],
},
],
}
type NavItem = (typeof data.navMain)[number]
type NavChild = NavItem['items'][number] & { isActive?: boolean }
export function Sidebar03() {
return (
<Sidebar data-slot="sidebar-03">
<SidebarHeader>
<VersionSwitcher versions={data.versions} defaultVersion={data.versions[0]!} />
<SearchForm />
</SidebarHeader>
<SidebarContent className="gap-0">
{data.navMain.map((item) => (
<Collapsible key={item.title} title={item.title} defaultOpen className="group/collapsible">
<SidebarGroup>
<SidebarGroupLabel
asChild
className="group/label text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground text-sm"
>
<CollapsibleTrigger>
{item.title}
<ChevronRight className="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-90" />
</CollapsibleTrigger>
</SidebarGroupLabel>
<CollapsibleContent>
<SidebarGroupContent>
<SidebarMenu>
{(item.items as NavChild[]).map((childItem) => (
<SidebarMenuItem key={childItem.title}>
<SidebarMenuButton asChild isActive={childItem.isActive}>
<a href={item.url}>{childItem.title}</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</CollapsibleContent>
</SidebarGroup>
</Collapsible>
))}
</SidebarContent>
<SidebarRail />
</Sidebar>
)
}
'use client'
import { Search } from 'lucide-react'
import { Label } from '@/components/ui/label'
import { SidebarGroup, SidebarGroupContent, SidebarInput } from '@/components/ui/sidebar'
export function SearchForm() {
return (
<form data-slot="sidebar-03">
<SidebarGroup className="py-0">
<SidebarGroupContent>
<Label htmlFor="search" className="sr-only">
Search
</Label>
<SidebarInput
id="search"
type="text"
placeholder="Search the docs..."
allowClear
prefix={<Search className="text-muted-foreground size-3.5" />}
/>
</SidebarGroupContent>
</SidebarGroup>
</form>
)
}
'use client'
import * as React from 'react'
import { Check, ChevronsUpDown } from 'lucide-react'
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'
import { SidebarMenu, SidebarMenuButton, SidebarMenuItem } from '@/components/ui/sidebar'
export interface VersionSwitcherProps {
versions: string[]
defaultVersion: string
}
export function VersionSwitcher({ versions, defaultVersion }: VersionSwitcherProps) {
const [selectedVersion, setSelectedVersion] = React.useState(defaultVersion)
return (
<SidebarMenu data-slot="sidebar-03">
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<SidebarMenuButton
size="lg"
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
<div className="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-lg">
<svg viewBox="0 0 24 24" className="size-4" fill="currentColor" aria-hidden="true">
<rect x="2" y="2" width="9" height="9" rx="1.5" />
<rect x="13" y="2" width="9" height="9" rx="1.5" opacity="0.55" />
<rect x="2" y="13" width="9" height="9" rx="1.5" opacity="0.55" />
<rect x="13" y="13" width="9" height="9" rx="1.5" />
</svg>
</div>
<div className="flex flex-col gap-0.5 leading-none">
<span className="font-medium">uipkge</span>
<span className="">v{selectedVersion}</span>
</div>
<ChevronsUpDown className="ml-auto" />
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-(--radix-dropdown-menu-trigger-width)" align="start">
{versions.map((version) => (
<DropdownMenuItem key={version} onSelect={() => setSelectedVersion(version)}>
v{version}
{selectedVersion === version ? <Check className="ml-auto" /> : null}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
)
}
// Demo page wired by `npx shadcn@latest add https://uipkge.dev/r/react/sidebar-03.json`.
// Visit /sidebar-03-demo after install to see the block without
// editing your own pages. Free to edit or delete me.
import { Sidebar03 } from '@/components/blocks/sidebar-03/Sidebar03'
import { SidebarInset, SidebarProvider, SidebarTrigger } from '@/components/ui/sidebar'
export default function Page() {
return (
<SidebarProvider data-slot="sidebar-03">
<Sidebar03 />
<SidebarInset>
<header className="border-border flex h-14 shrink-0 items-center gap-3 border-b px-4">
<SidebarTrigger className="size-7" />
<h1 className="text-sm font-semibold">sidebar-03 demo</h1>
</header>
<div className="flex-1 p-6">
<div className="grid auto-rows-min gap-4 md:grid-cols-3">
<div className="bg-muted/50 aspect-video rounded-xl" />
<div className="bg-muted/50 aspect-video rounded-xl" />
<div className="bg-muted/50 aspect-video rounded-xl" />
</div>
<div className="bg-muted/50 mt-4 min-h-[60vh] rounded-xl" />
</div>
</SidebarInset>
</SidebarProvider>
)
}
Raw manifest:https://uipkge.dev/r/react/sidebar-03.json