Get up and running with UI Builder in minutes. This guide covers installation, basic setup, and your first working editor.
Before installing UI Builder, ensure your project meets these requirements:
.npmrc workaround)init command below to set up both)If you haven't set up shadcn/ui yet, the installation command below can initialize it for you.
| Prop | Type | Description |
|---|---|---|
componentRegistry | ComponentRegistry | Maps component names to their definitions (see Components Intro) |
| Prop | Type | Default | Description |
|---|---|---|---|
initialLayers | ComponentLayer[] | [] | Initial page structure (e.g., from database) |
onChange | LayerChangeHandler | - | Callback when pages change (for persistence) |
initialVariables | Variable[] | [] | Initial variables for dynamic content |
onVariablesChange | VariableChangeHandler | - | Callback when variables change |
persistLayerStore | boolean | true | Enable localStorage persistence |
| Prop | Type | Description |
|---|---|---|
blocks | BlockRegistry | Block templates for the Blocks tab (see Shadcn Registry) |
functionRegistry | FunctionRegistry | Event handler functions that can be bound to component props (see Function Registry) |
| Prop | Type | Default | Description |
|---|---|---|---|
allowVariableEditing | boolean | true | Allow users to create/edit/delete variables |
allowPagesCreation | boolean | true | Allow users to create new pages |
allowPagesDeletion | boolean | true | Allow users to delete pages |
These props customize the default NavBar. If you provide a custom navBar in panelConfig, these are ignored.
| Prop | Type | Default | Description |
|---|---|---|---|
navLeftChildren | ReactNode | - | Content to render on the left side of the NavBar |
navRightChildren | ReactNode | - | Content to render on the right side of the NavBar |
showExport | boolean | true | Whether to show the Export button |
| Prop | Type | Description |
|---|---|---|
panelConfig | PanelConfig | Customize editor panels (see Panel Configuration) |
Note: Only componentRegistry is required. All other props are optional with sensible defaults.
Now that you have UI Builder running, explore these key areas:
If you are using shadcn/ui in your project, install the component directly from the registry:
bash1npx shadcn@latest add https://raw.githubusercontent.com/olliethedev/ui-builder/main/registry/block-registry.json
Or start a new Next.js project with UI Builder:
bash1npx shadcn@latest init https://raw.githubusercontent.com/olliethedev/ui-builder/main/registry/block-registry.json --base-color zinc
Install the full shadcn component library with 54 pre-configured components and 124 block templates:
bash1npx shadcn@latest add https://raw.githubusercontent.com/olliethedev/ui-builder/main/registry/shadcn-components-registry.json
See Shadcn Registry for details on using these components and blocks.
Note: You need to use style variables to have page theming working correctly.
If you encounter peer dependency warnings during installation (common with React 19 projects), create a .npmrc file in your project root:
bash1echo "legacy-peer-deps=true" > .npmrc
Then re-run the installation command.
After running the installation command, these files are added to your project:
| Location | Contents |
|---|---|
components/ui/ui-builder/ | Main UIBuilder component, LayerRenderer, ServerLayerRenderer, and types |
lib/ui-builder/ | Store (Zustand), registry definitions, utilities, and context providers |
hooks/ | Custom hooks (use-store, use-debounce, use-keyboard-shortcuts, etc.) |
components/ui/auto-form/ | Auto-generated forms for component props editing |
Auto-installed dependencies:
zustand - State managementsonner - Toast notifications@dnd-kit/* - Drag and drop functionalityzod - Schema validationCreate a new page file and paste this complete example:
tsx1// app/builder/page.tsx - Complete working example 2"use client"; 3 4import UIBuilder from "@/components/ui/ui-builder"; 5import { primitiveComponentDefinitions } from "@/lib/ui-builder/registry/primitive-component-definitions"; 6import { complexComponentDefinitions } from "@/lib/ui-builder/registry/complex-component-definitions"; 7 8const componentRegistry = { 9 ...primitiveComponentDefinitions, // div, span, img, etc. 10 ...complexComponentDefinitions, // Button, Badge, Card, etc. 11}; 12 13export default function BuilderPage() { 14 return ( 15 <main className="h-screen"> 16 <UIBuilder componentRegistry={componentRegistry} /> 17 </main> 18 ); 19}
Then visit http://localhost:3000/builder to see your editor.
What's included:
primitiveComponentDefinitions - HTML elements: div, span, h1-h3, p, ul, ol, li, img, iframe, acomplexComponentDefinitions - React components: Button, Badge, Card, Icon, Flexbox, Grid, Markdown, Accordion, etc.This gives you a full visual editor with pre-built shadcn/ui components.
For real applications, you'll want to control the initial state and persist changes:
tsx1import UIBuilder from "@/components/ui/ui-builder"; 2import { ComponentLayer, Variable } from "@/components/ui/ui-builder/types"; 3 4// Initial page structure 5const initialLayers: ComponentLayer[] = [ 6 { 7 id: "welcome-page", 8 type: "div", 9 name: "Welcome Page", 10 props: { 11 className: "p-8 min-h-screen flex flex-col gap-6", 12 }, 13 children: [ 14 { 15 id: "title", 16 type: "h1", 17 name: "Page Title", 18 props: { 19 className: "text-4xl font-bold text-center", 20 }, 21 children: "Welcome to UI Builder!", 22 }, 23 { 24 id: "cta-button", 25 type: "Button", 26 name: "CTA Button", 27 props: { 28 variant: "default", 29 className: "mx-auto w-fit", 30 }, 31 children: [{ 32 id: "button-text", 33 type: "span", 34 name: "Button Text", 35 props: {}, 36 children: "Get Started", 37 }], 38 }, 39 ], 40 }, 41]; 42 43// Variables for dynamic content 44const initialVariables: Variable[] = [ 45 { 46 id: "welcome-msg", 47 name: "welcomeMessage", 48 type: "string", 49 defaultValue: "Welcome to UI Builder!" 50 } 51]; 52 53export function AppWithState() { 54 const handleLayersChange = (updatedLayers: ComponentLayer[]) => { 55 // Save to database, localStorage, etc. 56 console.log("Layers updated:", updatedLayers); 57 }; 58 59 const handleVariablesChange = (updatedVariables: Variable[]) => { 60 // Save to database, localStorage, etc. 61 console.log("Variables updated:", updatedVariables); 62 }; 63 64 return ( 65 <UIBuilder 66 componentRegistry={componentRegistry} 67 initialLayers={initialLayers} 68 onChange={handleLayersChange} 69 initialVariables={initialVariables} 70 onVariablesChange={handleVariablesChange} 71 /> 72 ); 73}
All types are exported from @/components/ui/ui-builder/types:
tsx1import type { 2 // Core types 3 ComponentLayer, // Page/layer structure 4 ComponentRegistry, // Component definitions map 5 RegistryEntry, // Single component definition 6 7 // Variables & Binding 8 Variable, // Variable definition 9 VariableReference, // { __variableRef: string } 10 VariableValueType, // 'string' | 'number' | 'boolean' | 'function' 11 DefaultVariableBinding, // Auto-binding configuration 12 13 // Functions & Blocks 14 FunctionRegistry, // Event handler functions 15 FunctionDefinition, // Single function definition 16 BlockRegistry, // Block templates 17 BlockDefinition, // Single block definition 18 19 // Handlers 20 LayerChangeHandler, // onChange callback type 21 VariableChangeHandler, // onVariablesChange callback type 22 23 // Field overrides 24 FieldConfigFunction, // Custom form field configuration 25 AutoFormInputComponentProps, // Props for custom field components 26} from "@/components/ui/ui-builder/types"; 27 28// Helper functions 29import { 30 isVariableReference, // Check if value is a variable reference 31 createVariable, // Type-safe variable creation 32} from "@/components/ui/ui-builder/types";
To display pages in production without the editor interface, use LayerRenderer (client) or ServerLayerRenderer (SSR/RSC):
tsx1import LayerRenderer from "@/components/ui/ui-builder/layer-renderer"; 2 3// Basic rendering (client component) 4export function MyPage({ page }) { 5 return ( 6 <LayerRenderer 7 page={page} 8 componentRegistry={componentRegistry} 9 /> 10 ); 11} 12 13// With variables for dynamic content 14export function DynamicPage({ page, userData }) { 15 const variableValues = { 16 "welcome-msg": `Welcome back, ${userData.name}!` 17 }; 18 19 return ( 20 <LayerRenderer 21 page={page} 22 componentRegistry={componentRegistry} 23 variables={variables} 24 variableValues={variableValues} 25 /> 26 ); 27}
For React Server Components or SSG, use ServerLayerRenderer:
tsx1import { ServerLayerRenderer } from "@/components/ui/ui-builder/server-layer-renderer"; 2 3// Server Component (no 'use client' needed) 4export default async function MyPage() { 5 const page = await fetchPageFromDB(); 6 return ( 7 <ServerLayerRenderer 8 page={page} 9 componentRegistry={componentRegistry} 10 /> 11 ); 12}
🎯 Try it: Check out the Renderer Demo, Variables Demo, and SSR Demo to see the renderers in action.
Problem: Import errors like Cannot find module '@/components/ui/ui-builder'
Solutions:
components.json exists in your project root@/ alias is configured in tsconfig.jsonProblem: Components look unstyled or broken
Solutions:
globals.css includes the shadcn theme variablestailwind.config.js includes the UI Builder paths:js1content: [ 2 './components/**/*.{js,ts,jsx,tsx}', 3 './lib/**/*.{js,ts,jsx,tsx}', 4]
Problem: Added component doesn't appear or shows error
Solutions:
componentRegistrydefaultChildren are in the registryProblem: Type errors when using UI Builder components
Solutions:
@/components/ui/ui-builder/typesComponentRegistry type for your registry objectComponentLayer[] for initial layersProblem: npm warns about peer dependency conflicts
Solution: Create .npmrc with legacy-peer-deps=true (see Installation section)