{"id":47702594,"url":"https://github.com/mdocui/mdocui","last_synced_at":"2026-04-11T09:09:39.325Z","repository":{"id":347319195,"uuid":"1193553519","full_name":"mdocui/mdocui","owner":"mdocui","description":"Generative UI for LLMs using Markdoc {% %} tag syntax","archived":false,"fork":false,"pushed_at":"2026-04-01T13:31:03.000Z","size":255,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-03T04:04:30.591Z","etag":null,"topics":["ai","generative-ui","llm","markdoc","react","streaming","ui-components"],"latest_commit_sha":null,"homepage":"https://mdocui.github.io","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mdocui.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-27T10:53:14.000Z","updated_at":"2026-04-01T13:32:05.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mdocui/mdocui","commit_stats":null,"previous_names":["mdocui/mdocui"],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/mdocui/mdocui","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdocui%2Fmdocui","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdocui%2Fmdocui/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdocui%2Fmdocui/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdocui%2Fmdocui/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mdocui","download_url":"https://codeload.github.com/mdocui/mdocui/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdocui%2Fmdocui/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31674679,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-11T08:18:19.405Z","status":"ssl_error","status_checked_at":"2026-04-11T08:17:08.892Z","response_time":54,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","generative-ui","llm","markdoc","react","streaming","ui-components"],"created_at":"2026-04-02T17:42:00.746Z","updated_at":"2026-04-11T09:09:39.319Z","avatar_url":"https://github.com/mdocui.png","language":"TypeScript","funding_links":["https://github.com/sponsors/pnutmath"],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/mdocui/.github/main/assets/logo.png\" alt=\"mdocUI\" width=\"400\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.npmjs.com/package/@mdocui/core\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/@mdocui/core?label=%40mdocui%2Fcore\u0026color=blue\" alt=\"npm core\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/@mdocui/react\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/@mdocui/react?label=%40mdocui%2Freact\u0026color=blue\" alt=\"npm react\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/@mdocui/cli\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/@mdocui/cli?label=%40mdocui%2Fcli\u0026color=blue\" alt=\"npm cli\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/mdocui/mdocui/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://github.com/mdocui/mdocui/actions/workflows/ci.yml/badge.svg\" alt=\"CI\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/mdocui/mdocui/actions/workflows/codeql.yml\"\u003e\u003cimg src=\"https://github.com/mdocui/mdocui/actions/workflows/codeql.yml/badge.svg\" alt=\"CodeQL\"\u003e\u003c/a\u003e\n  \u003ca href=\"LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/License-MIT-blue.svg\" alt=\"MIT License\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.typescriptlang.org/\"\u003e\u003cimg src=\"https://img.shields.io/badge/TypeScript-6.0-blue\" alt=\"TypeScript\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://nodejs.org/\"\u003e\u003cimg src=\"https://img.shields.io/badge/Node-%E2%89%A522-green\" alt=\"Node\"\u003e\u003c/a\u003e\n  \u003ca href=\"#\"\u003e\u003cimg src=\"https://img.shields.io/badge/Status-Alpha-orange\" alt=\"Alpha\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/sponsors/pnutmath\"\u003e\u003cimg src=\"https://img.shields.io/github/sponsors/pnutmath?style=flat-square\u0026label=Sponsor\" alt=\"Sponsor pnutmath\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://mdocui.github.io\"\u003eDocumentation\u003c/a\u003e · \u003ca href=\"https://mdocui.vercel.app\"\u003eLive Demo\u003c/a\u003e · \u003ca href=\"https://www.npmjs.com/org/mdocui\"\u003enpm\u003c/a\u003e\n\u003c/p\u003e\n\n\u003e **Alpha** -- mdocUI is under active development. The API may change between minor versions. We follow [semver](https://semver.org/) and will reach 1.0 once the API stabilizes.\n\nGenerative UI library for LLMs using Markdoc `{% %}` tag syntax inline with markdown prose.\n\nLLMs write natural markdown **and** drop interactive UI components in the same stream — charts, buttons, forms, tables, cards, and more. No custom DSL to learn, no JSON blocks, no JSX confusion.\n\n**Key features:** built-in prose rendering, component merging, CLI scaffolder, error boundaries, streaming animations, shimmer placeholders, prop validation, context data passthrough, and configurable prompt verbosity.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/mdocui/.github/main/assets/demo.gif\" alt=\"mdocUI demo — streaming dashboard with charts, stats, and tables\" width=\"800\"\u003e\n\u003c/p\u003e\n\n```\nThe Q4 results show strong growth across all segments.\n\n{% chart type=\"bar\" labels=[\"Jan\",\"Feb\",\"Mar\"] values=[120,150,180] /%}\n\nRevenue grew **12%** quarter-over-quarter.\n\n{% callout type=\"info\" title=\"Action Required\" %}\nReview the pipeline before end of quarter.\n{% /callout %}\n\n{% button action=\"continue\" label=\"Show by region\" /%}\n{% button action=\"continue\" label=\"Export as PDF\" /%}\n```\n\n## Syntax: Markdown + Markdoc Tags\n\nmdocUI combines two syntaxes in a single stream:\n\n- **[Markdown](https://commonmark.org/)** -- the universal prose format created by John Gruber (2004), standardized by [CommonMark](https://github.com/commonmark/commonmark-spec). Handles headings, bold, italic, lists, links, code blocks -- everything an LLM already knows how to write.\n- **[Markdoc](https://github.com/markdoc/markdoc) `{% %}` tags** -- the tag syntax from Stripe's Markdoc framework (2022, MIT). Markdoc extends Markdown with `{% %}` custom tags for structured content.\n\nmdocUI borrows **only the `{% %}` tag syntax** from Markdoc. We do not use Markdoc's parser, runtime, compiler, schema system, or config layer. We built our own streaming parser from scratch, purpose-built for token-by-token LLM output.\n\n### Tag forms\n\nSelf-closing (no body):\n\n```\n{% tagname attr=\"value\" /%}\n```\n\nWith body content:\n\n```\n{% tagname attr=\"value\" %}\nBody content here -- can include markdown or nested tags.\n{% /tagname %}\n```\n\n### Why `{% %}` works for streaming\n\nThe character sequence `{%` never appears in normal prose, standard markdown, or fenced code blocks. This makes it a reliable delimiter that a character-by-character streaming parser can detect without ambiguity -- no lookahead, no backtracking, no fragile heuristics.\n\nThe LLM writes both markdown **and** component tags in the same response. The parser separates them into prose nodes and component nodes as tokens arrive.\n\n## Why mdocUI?\n\n| Approach | Prose? | Components? | Streaming? | Token efficient? |\n|----------|--------|-------------|------------|------------------|\n| Plain markdown | Yes | No | Yes | Yes |\n| OpenUI Lang | No | Yes | Yes | Yes |\n| JSON blocks in markdown | Yes | Yes | Fragile | No |\n| JSX in markdown | Yes | Yes | Fragile | No |\n| **mdocUI** | **Yes** | **Yes** | **Yes** | **Yes** |\n\nMarkdoc's `{% %}` delimiters are unambiguous — they never appear in normal prose or code, making streaming parsing reliable.\n\n## Packages\n\n| Package | Description | Status |\n|---------|-------------|--------|\n| [`@mdocui/core`](packages/core) | Streaming parser, tokenizer, component registry, prompt generator | Alpha |\n| [`@mdocui/react`](packages/react) | React renderer, 24 default components, `useRenderer` hook | Alpha |\n| [`@mdocui/cli`](packages/cli) | Scaffold, generate system prompts, preview | Alpha |\n\n## Quick Start\n\n```bash\npnpm add @mdocui/core @mdocui/react\n```\n\n### 1. Generate a system prompt\n\n`generatePrompt()` merges two layers into one prompt: the **library layer** (tag syntax, component signatures, composition rules — auto-generated from the registry) and your **app layer** (preamble, domain rules, examples). You never write syntax docs manually.\n\n```typescript\nimport { generatePrompt } from '@mdocui/core'\nimport { createDefaultRegistry, defaultGroups } from '@mdocui/react'\n\nconst registry = createDefaultRegistry()\nconst systemPrompt = generatePrompt(registry, {\n  preamble: 'You are a helpful assistant.',\n  groups: defaultGroups,\n})\n// Pass systemPrompt to your LLM\n```\n\n### 2. Render streamed output\n\n```tsx\nimport { useRenderer } from '@mdocui/react'\nimport { Renderer, defaultComponents, createDefaultRegistry } from '@mdocui/react'\n\nconst registry = createDefaultRegistry()\n\nfunction Chat() {\n  const { nodes, isStreaming, push, done } = useRenderer({ registry })\n\n  // Call push(chunk) as tokens arrive from LLM\n  // Call done() when stream ends\n  // useRenderer batches renders to at most one per frame (~60fps) automatically\n\n  return (\n    \u003cRenderer\n      nodes={nodes}\n      components={defaultComponents}\n      isStreaming={isStreaming}\n      onAction={(event) =\u003e {\n        if (event.action === 'continue') {\n          sendMessage(event.label)\n        }\n      }}\n      onError={(event) =\u003e {\n        console.error(`Component ${event.componentName} failed:`, event.error)\n      }}\n    /\u003e\n  )\n}\n```\n\n### 3. Handle actions and errors\n\nEvery interactive component fires through a single `onAction` callback:\n\n```typescript\nonAction={(event) =\u003e {\n  switch (event.action) {\n    case 'continue':\n      // Send event.label as a new user message\n      break\n    case 'submit:formName':\n      // event.formState has all field values\n      break\n    case 'open_url':\n      // event.params.url has the URL\n      break\n  }\n}}\n```\n\nCatch component rendering errors with `onError`:\n\n```typescript\nonError={(event) =\u003e {\n  console.error(`${event.componentName} failed to render:`, event.error)\n  // event.props contains the props that caused the error\n}}\n```\n\n## Custom Components\n\nEvery component receives `ComponentProps` and can be swapped:\n\n```typescript\ninterface ComponentProps {\n  name: string\n  props: Record\u003cstring, unknown\u003e\n  children?: React.ReactNode\n  className?: string\n  onAction: ActionHandler\n  isStreaming: boolean\n}\n```\n\n### Override specific components\n\n```tsx\nimport { defaultComponents, Renderer } from '@mdocui/react'\n\nconst myComponents = {\n  ...defaultComponents,\n  button: MyButton,     // swap just the button\n  card: MyShadcnCard,   // use your shadcn card\n}\n\n\u003cRenderer nodes={nodes} components={myComponents} /\u003e\n```\n\n### Tailwind / className support\n\nPass per-component classes via `classNames`:\n\n```tsx\n\u003cRenderer\n  nodes={nodes}\n  components={defaultComponents}\n  classNames={{\n    button: 'bg-blue-500 hover:bg-blue-600 text-white rounded-lg px-4 py-2',\n    card: 'border border-gray-200 rounded-xl p-6 shadow-sm',\n    callout: 'border-l-4 pl-4 py-3',\n  }}\n/\u003e\n```\n\n### Bring your own components entirely\n\n```tsx\nconst shadcnComponents = {\n  button: ({ props, onAction }) =\u003e (\n    \u003cButton onClick={() =\u003e onAction({ type: 'button_click', action: props.action, label: props.label, tagName: 'button' })}\u003e\n      {props.label}\n    \u003c/Button\u003e\n  ),\n  card: ({ props, children }) =\u003e (\n    \u003cCard\u003e\u003cCardHeader\u003e{props.title}\u003c/CardHeader\u003e\u003cCardContent\u003e{children}\u003c/CardContent\u003e\u003c/Card\u003e\n  ),\n}\n\n\u003cRenderer nodes={nodes} components={shadcnComponents} /\u003e\n```\n\n## Architecture\n\n| Layer | Role |\n|-------|------|\n| **Tokenizer** | Character-by-character lexer, tracks `IN_PROSE` / `IN_TAG` / `IN_STRING` states |\n| **StreamingParser** | Buffers incomplete tags, merges prose, emits `ASTNode[]` |\n| **ComponentRegistry** | Validates tag names and props via Zod schemas |\n| **Renderer** | Maps AST nodes to React components with error boundaries and animations |\n\nThe core is framework-agnostic. `@mdocui/react` is one adapter — Vue, Svelte, and Angular adapters can follow the same pattern.\n\n## Available Components\n\n### Layout\n`stack` `grid` `card` `divider` `accordion` `tabs` `tab`\n\n### Interactive\n`button` `button-group` `input` `textarea` `select` `checkbox` `toggle` `form`\n\n### Data\n`chart` `table` `stat` `progress`\n\n### Content\n`callout` `badge` `image` `code-block` `link`\n\nAll components render theme-neutral semantic HTML with `data-mdocui-*` attributes. They use `currentColor` and `inherit` — no hardcoded colors. They adapt to any light or dark theme automatically. Style them with CSS, Tailwind `classNames`, or swap in your own components entirely.\n\n## Development\n\n```bash\n# Install\npnpm install\n\n# Build all packages\npnpm build\n\n# Run tests\npnpm test\n\n# Lint\npnpm lint\n\n# Run playground\npnpm playground\n```\n\n## Contributing\n\nContributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for the full guide.\n\n1. Fork the repo\n2. Create a feature branch (`git checkout -b feature/my-change`)\n3. Make your changes with tests\n4. Run `pnpm test \u0026\u0026 pnpm lint`\n5. Open a pull request\n\n## For AI Agents\n\n- [SKILL.md](SKILL.md) — implementation guide for AI coding agents\n- [llms.txt](https://mdocui.github.io/llms.txt) — machine-readable project summary for LLM crawlers\n\n**Install as a Claude Code skill:**\n\nProject-level (current project only):\n```bash\nmkdir -p .claude/skills/mdocui\ncurl -o .claude/skills/mdocui/SKILL.md https://raw.githubusercontent.com/mdocui/mdocui/main/SKILL.md\n```\n\nPersonal (available in all your projects):\n```bash\nmkdir -p ~/.claude/skills/mdocui\ncurl -o ~/.claude/skills/mdocui/SKILL.md https://raw.githubusercontent.com/mdocui/mdocui/main/SKILL.md\n```\n\nThen invoke with `/mdocui` in Claude Code.\n\n## Roadmap\n\n**Renderers**\n- `@mdocui/vue` — Vue 3 renderer\n- `@mdocui/svelte` — Svelte 5 renderer\n- `@mdocui/solid` — SolidJS renderer\n\n**Integrations**\n- `@mdocui/nextjs` — App Router helpers, RSC-safe imports, route handler template\n- `@mdocui/vercel-ai` — Vercel AI SDK `useChat` to `useRenderer` bridge\n\n**Developer tools**\n- `@mdocui/devtools` — browser panel for AST inspection, parse meta, streaming state\n- VS Code extension — syntax highlighting and autocomplete for `{% %}` tags\n\n**Milestone**\n- v1.0.0 — stable API, frozen component props, CHANGELOG\n\nHave an idea? [Open a suggestion issue](https://github.com/mdocui/mdocui/issues/new?labels=enhancement\u0026title=Suggestion:+).\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmdocui%2Fmdocui","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmdocui%2Fmdocui","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmdocui%2Fmdocui/lists"}