--- title: "DevState: Extensible Debug Renderer" date: "2025-10-21" draft: true --- # DevState: Extensible Debug Renderer Render complex runtime data structures with type-specific handlers. Register custom renderers to format atoms, queries, and domain objects without touching the core. ## Registry Pattern Define custom renderers with type guards: ```tsx export function defineRenderer( test: (data: UnknownObject) => data is T, render: Renderer, ): RegistryItem { return { test, render }; } ``` ```tsx // Match by type predicate defineRenderer( (data) => isAtom(data), ({ data, showPath, r }) => { const value = useAtomValue(data); return ; }, ); ``` String-based renderers parse and transform: ```tsx export function defineStringRenderer( parse: (data: string) => null | undefined | T, render: Renderer, ): StringRegistryItem { return { parse, render }; } ``` ```tsx // Parse UIDs from strings defineStringRenderer( (data) => (World.isUID(data) ? data : null), ({ data, r, showPath }) => ( ), ); ``` ## Renderer Components Built-in primitives handle common cases: ```tsx const r: RendererComponents = { Array: , Object: , Unknown: , Placeholder: , Custom: ( ... ), }; ``` Use `commaSeparatedOmitKeys` to filter object keys without memoizing the data object. ## Integration Register renderers at app root: ```tsx export const ProvideAppDevStateRenderers = ({ children }: { children: React.ReactNode }) => { return ( {children} ); }; ``` ```tsx const RENDERERS: RegistryItem[] = [ defineRenderer( (thing) => thing instanceof DevString, ({ data, r }) => ..., ), defineRenderer( objectPredicateMacro(["label", "hash"], (d) => d._tag === "db"), ({ data, r }) => , ), ]; export const ProvideAppDevStateRenderers = ({ children }) => ( {children} ); ``` ## Real Examples **Jotai Atoms**: Read atom value and render live ```tsx defineRenderer( (thing) => isAtom(thing), ({ data, r }) => { const value = useAtomValue(data); return ; }, ); ``` **DevString with location links**: ```tsx defineRenderer( (thing) => thing instanceof DevString, ({ data, r, showPath }) => { const { context, message } = data.toJSON(); return ( }> {message.map((part, i) => typeof part === "string" ? {part} : , )} {data.cause && } ); }, ); ``` ## Key Features - **Type-safe matching**: Type guards ensure correct data shape - **Composable**: Renderers use `r.*` components to recurse - **React hooks**: Use atoms, queries, state inside renderers - **Depth tracking**: Automatic collapse at configurable depth - **Circular detection**: Prevents infinite recursion - **Ordered keys**: Control property display order globally