Component Registry
The component registry pattern allows you to define window components once and open them by ID. This makes window state fully serializable—perfect for persistence in localStorage or URL state.
Setting Up with defineWindows
The recommended approach is to use defineWindows for unified configuration:
import { defineWindows } from 'glazier/server';
// Define all windows in one placeconst windows = defineWindows({ settings: { title: 'Settings', defaultPosition: { x: 100, y: 100 }, defaultSize: { width: 350, height: 400 }, path: '/settings', icon: { label: 'Settings', iconKey: 'settings', position: { x: 20, y: 20 }, }, }, terminal: { title: 'Terminal', defaultPosition: { x: 150, y: 150 }, defaultSize: { width: 600, height: 400 }, path: '/terminal', defaultProps: { initialPath: '/home/user' }, }, notes: { title: 'Notes', defaultPosition: { x: 200, y: 200 }, defaultSize: { width: 400, height: 500 }, path: '/notes', },});
export type WindowId = typeof windows.ids[number];Creating a Type-Safe Registry
Use createRegistry to ensure all window IDs have corresponding components:
import { createRegistry } from 'glazier';
// Define your window componentsfunction SettingsPanel({ windowId }: { windowId: string }) { return ( <WindowFrame windowId={windowId}> <TitleBar><Title /><WindowControls /></TitleBar> <Content><h3>Settings</h3>{/* Settings UI */}</Content> <ResizeHandles windowId={windowId} /> </WindowFrame> );}
function TerminalApp({ windowId, initialPath }: { windowId: string; initialPath?: string }) { return ( <WindowFrame windowId={windowId}> <TitleBar><Title /><WindowControls /></TitleBar> <Content> <code>$ cd {initialPath ?? '/home/user'}</code> </Content> <ResizeHandles windowId={windowId} /> </WindowFrame> );}
function NotesApp({ windowId }: { windowId: string }) { return ( <WindowFrame windowId={windowId}> <TitleBar><Title /><WindowControls /></TitleBar> <Content> <textarea placeholder="Start typing..." /> </Content> <ResizeHandles windowId={windowId} /> </WindowFrame> );}
// Create type-safe registry - TypeScript ensures all IDs are coveredconst registry = createRegistry(windows.ids, { settings: SettingsPanel, terminal: TerminalApp, notes: NotesApp,});Using the Registry
Pass the registry to WindowManagerProvider:
import { WindowManagerProvider, Desktop, Window } from 'glazier';
function App() { const containerRef = useRef<HTMLDivElement>(null);
return ( <WindowManagerProvider boundsRef={containerRef} registry={registry} defaultWindows={[windows.getWindowState('settings')]} defaultIcons={windows.getIconConfigs()} > <div ref={containerRef} className="h-screen relative"> <Desktop> {({ windowId, component: Component, componentProps }) => ( <Window id={windowId} className="rounded-lg shadow-xl"> <Component windowId={windowId} {...componentProps} /> </Window> )} </Desktop> </div> </WindowManagerProvider> );}Opening Windows by Component ID
Use openWindow with the window state from defineWindows:
const { openWindow } = useWindowManager();
function launchApp(componentId: WindowId) { // Get default state from defineWindows const state = windows.getWindowState(componentId); openWindow(state);}
// Or with custom overridesfunction launchTerminal(initialPath: string) { openWindow({ ...windows.getWindowState('terminal'), id: `terminal-${Date.now()}`, // Unique ID for multiple instances componentProps: { initialPath }, });}URL Routing
Sync window focus with browser URL using createBrowserAdapter:
import { createBrowserAdapter } from 'glazier';
const routingAdapter = createBrowserAdapter();const pathMap = windows.getPathMap();
<WindowManagerProvider registry={registry} onFocusChange={(windowId) => { if (windowId) { const path = pathMap[windowId as WindowId]; if (path) routingAdapter.navigate(path); } }}>Server-Side Rendering / Static Generation
For SSR/SSG frameworks like Next.js or Astro, import defineWindows from glazier/server:
import { defineWindows } from 'glazier/server'; // Server-safe import
export const windows = defineWindows({ home: { title: 'Home', path: '/', ... }, about: { title: 'About', path: '/about', ... },});
// Use in server componentsexport const validSlugs = windows.getValidSlugs();
// pages/[slug].tsxexport function getStaticPaths() { return validSlugs.map(slug => ({ params: { slug } }));}Serializable State
Because windows reference components by ID, the state is JSON-serializable:
// Window state is fully serializableconst windowState = { id: 'terminal-123', title: 'Terminal', componentId: 'terminal', componentProps: { initialPath: '/home/user' }, position: { x: 100, y: 100 }, size: { width: 600, height: 400 }, zIndex: 1, displayState: 'normal',};
// Save to localStoragelocalStorage.setItem('windows', JSON.stringify(state.windows));
// Restore on loadconst saved = JSON.parse(localStorage.getItem('windows') || '[]');<WindowManagerProvider defaultWindows={saved} registry={registry}>Complete Example
import { useRef, useState } from 'react';import { WindowManagerProvider, Desktop, Window, Taskbar, SnapPreviewOverlay, WindowFrame, TitleBar, Title, WindowControls, Content, ResizeHandles, createRegistry, createBrowserAdapter,} from 'glazier';import { defineWindows } from 'glazier/server';
// Configurationconst windows = defineWindows({ settings: { title: 'Settings', defaultPosition: { x: 50, y: 80 }, defaultSize: { width: 300, height: 250 }, path: '/settings', icon: { label: 'Settings', position: { x: 20, y: 20 } }, }, terminal: { title: 'Terminal', defaultPosition: { x: 100, y: 120 }, defaultSize: { width: 600, height: 400 }, path: '/terminal', },});
// Registryconst registry = createRegistry(windows.ids, { settings: SettingsPanel, terminal: TerminalApp,});
// Routingconst routingAdapter = createBrowserAdapter();const pathMap = windows.getPathMap();
function App() { const containerRef = useRef<HTMLDivElement>(null); const [snapZone, setSnapZone] = useState<'left' | 'right' | null>(null);
return ( <WindowManagerProvider boundsRef={containerRef} registry={registry} defaultWindows={[windows.getWindowState('settings')]} defaultIcons={windows.getIconConfigs()} onFocusChange={(windowId) => { if (windowId) { const path = pathMap[windowId as typeof windows.ids[number]]; if (path) routingAdapter.navigate(path); } }} > <div ref={containerRef} className="h-screen relative"> <Desktop> {({ windowId, component: Component, componentProps }) => ( <Window id={windowId} className="rounded-lg shadow-xl bg-slate-800"> <Component windowId={windowId} onSnapZoneChange={setSnapZone} {...componentProps} /> </Window> )} </Desktop>
<SnapPreviewOverlay zone={snapZone} /> <MyTaskbar /> </div> </WindowManagerProvider> );}