Skip to content

Add @openuidev/svelte-lang and extract @openuidev/lang-core#347

Merged
abhithesys merged 14 commits intothesysdev:mainfrom
shipooor:feature/svelte-lang
Mar 25, 2026
Merged

Add @openuidev/svelte-lang and extract @openuidev/lang-core#347
abhithesys merged 14 commits intothesysdev:mainfrom
shipooor:feature/svelte-lang

Conversation

@shipooor
Copy link
Contributor

@shipooor shipooor commented Mar 16, 2026

Closes #302

What

Adds @openuidev/svelte-lang — a Svelte 5 renderer for OpenUI Lang — and extracts the framework-agnostic core into @openuidev/lang-core, as suggested by @AbhinRustagi in this comment.

Architecture

@openuidev/lang-core          (parser, prompt, validation, generic Library<C>)
       ↑              ↑
@openuidev/react-lang    @openuidev/svelte-lang
  (thin React wrapper)     (thin Svelte 5 wrapper)
  • lang-core holds all framework-agnostic code: parser, prompt generation, validation, and a generic Library<C> / DefinedComponent<T, C> system where C is the framework component type.
  • react-lang narrows C to React.FC and re-exports everything. Public API is unchanged — this is a transparent refactor.
  • svelte-lang narrows C to Component<Props> and implements the Svelte 5 rendering layer.

This structure makes adding future framework adapters (Vue, Solid, etc.) straightforward.

Changes

1. @openuidev/lang-core (new package)

  • Parser (createParser, createStreamingParser) moved from react-lang
  • Prompt generation (generatePrompt) moved from react-lang
  • Validation (validate, parseRules, builtInValidators) moved from react-lang
  • Generic Library<C> and DefinedComponent<T, C> types

2. @openuidev/react-lang (refactored, no API change)

  • Imports parser/prompt/validation from lang-core instead of local files
  • library.ts is now a thin wrapper that narrows C to React.FC
  • All public exports remain identical

3. @openuidev/svelte-lang (new package)

  • Renderer.svelte — streaming renderer using $derived for reactive parsing
  • RenderNode.svelte — recursive component renderer with <svelte:boundary> error handling
  • library.tsdefineComponent / createLibrary with Svelte 5 types
  • context.svelte.ts — context API (getOpenUIContext, getTriggerAction, etc.)
  • validation.svelte.ts — form validation with $state-backed reactive errors
  • 30 tests (Vitest + @testing-library/svelte)

4. examples/svelte-chat (new example)

  • SvelteKit app with 4 components (Stack, Card, TextContent, Button)
  • Demonstrates action handling and nested component rendering

Svelte 5 features used

  • $props(), $state, $derived, $effect runes
  • {#snippet} / {@render} for recursive rendering (renderNode passed as snippet prop)
  • <svelte:boundary> for per-component error isolation
  • getContext / setContext with Symbol keys and getter pattern for reactivity

React → Svelte API mapping

React Svelte Notes
useOpenUI() getOpenUIContext() Svelte uses getContext, not hooks
useRenderNode() snippet prop Avoids stale closures
useTriggerAction() getTriggerAction()
useIsStreaming() getIsStreaming()
useState / useMemo $state / $derived
useEffect $effect
ErrorBoundary class <svelte:boundary> Shows nothing on error (no last-valid-children equivalent in Svelte)

Verification

pnpm --filter @openuidev/lang-core run build       # tsc clean
pnpm --filter @openuidev/react-lang run build       # tsc clean, API unchanged
pnpm --filter @openuidev/svelte-lang run build      # svelte-package clean
pnpm --filter @openuidev/svelte-lang run check      # svelte-check: 0 errors
pnpm --filter @openuidev/svelte-lang run test       # 30/30 tests pass
pnpm --filter svelte-chat run build                 # vite build clean
pnpm --filter openui-chat run build                 # existing example still works
pnpm --filter shadcn-chat run build                 # existing example still works

@shipooor shipooor force-pushed the feature/svelte-lang branch 2 times, most recently from be5c41e to 70c5c87 Compare March 17, 2026 03:26
@Aditya-thesys Aditya-thesys self-requested a review March 20, 2026 11:30
Move parser, prompt generation, validation, and generic library
types out of react-lang into a new @openuidev/lang-core package.
The generic DefinedComponent<T, C = unknown> and Library<C> allow
each framework adapter (React, Svelte, etc.) to narrow the component
type parameter independently.
Replace local parser, prompt, and validation code with imports from
@openuidev/lang-core. The library.ts becomes a thin wrapper that
narrows the generic C parameter to React.FC<ComponentRenderProps>.
Public API is unchanged — all existing examples build without
modification.
Introduce @openuidev/svelte-lang — a Svelte 5 port of react-lang
built on top of @openuidev/lang-core. Uses runes ($state, $derived,
$effect, $props), snippets for renderNode, getContext/setContext for
the OpenUI context, and <svelte:boundary> for error handling.
Includes 30 passing tests.
A SvelteKit demo app showing @openuidev/svelte-lang in action with
mock streaming, four components (Stack, Card, TextContent, Button),
and action event handling. No API key required. Also add .svelte-kit
to .gitignore.
@shipooor shipooor force-pushed the feature/svelte-lang branch from 70c5c87 to c45b238 Compare March 20, 2026 14:13
Copy link
Contributor

@abhithesys abhithesys left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ran the example, chat ui isn't working. At high level PR looks good. Can you update the example to use OpenAI api, while we are reviewing.

This covers:
- New eslint.config.cjs and tsconfig.test.json for lang-core (matching react-lang pattern)
- Parser test file moved from react-lang to lang-core
- package.json updated with vitest dev dependency and formatting
- src/index.ts export updated (ValidationError → ValidationErrorCode)
@abhithesys
Copy link
Contributor

@shipooor I m making minor changes required in this PR to unblock other Renderers.

…and useSetDefaultValue

    - RenderNode: auto-retry rendering when props change after an error
      (captures reset fn from svelte:boundary and calls it on prop change,
      with untrack to prevent infinite loops)
    - getIsStreaming: return () => boolean getter instead of a plain boolean
      snapshot so consumers get reactive updates
    - useSetDefaultValue: read existing value from form state inside
      instead of accepting a stale snapshot parameter, fixing reactivity
    - README: add children rendering, parser errors, context reactivity
      note, streaming parser docs, types, and React comparison table
@abhithesys abhithesys merged commit 4634507 into thesysdev:main Mar 25, 2026
1 check passed
@github-project-automation github-project-automation bot moved this from Next to Later in OpenUI Roadmap Mar 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Later

Development

Successfully merging this pull request may close these issues.

Svelte Renderer

4 participants