diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 98f0ce5f..53b7ecb3 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -87,7 +87,34 @@ jobs:
run: pnpm run build
- name: Publish all public packages
- run: pnpm -r publish --provenance --no-git-checks --access public --filter '@json-render/*'
+ run: |
+ LOCAL_VERSION="${{ needs.check-release.outputs.version }}"
+ FAILED=""
+
+ publish_pkg() {
+ local dir="$1" name="$2"
+ REGISTRY_VERSION=$(npm view "$name" version 2>/dev/null || echo "0.0.0")
+ if [ "$LOCAL_VERSION" = "$REGISTRY_VERSION" ]; then
+ echo "$name@$LOCAL_VERSION already published, skipping"
+ return 0
+ fi
+ echo "Publishing $name@$LOCAL_VERSION..."
+ TARBALL=$(cd "$dir" && pnpm pack --pack-destination /tmp | tail -1)
+ if ! npm publish "$TARBALL" --provenance --access public; then
+ FAILED="$FAILED $name"
+ fi
+ }
+
+ for dir in packages/*/; do
+ PKG_NAME=$(node -p "try { const p = require('./$dir/package.json'); p.private ? '' : p.name } catch { '' }")
+ [ -z "$PKG_NAME" ] && continue
+ publish_pkg "$dir" "$PKG_NAME"
+ done
+
+ if [ -n "$FAILED" ]; then
+ echo "Failed to publish:$FAILED"
+ exit 1
+ fi
github-release:
name: Create GitHub Release
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 880263d4..6cc8c37a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,10 +1,26 @@
# Changelog
-## 0.18.0
+## 0.19.0
### New Features
+- **Custom directives API** — `@json-render/core` now supports custom directives via `defineDirective`, letting you declare new JSON shapes (like `$format`, `$math`) that resolve to computed values at render time. Directives compose naturally — nest `$format` over `$math` over `$state` and they resolve inside-out. All four renderers (React, Vue, Svelte, Solid) have built-in directive resolution (#279)
+- **`@json-render/directives`** — New package shipping seven ready-made directives: `$format` (date, currency, number, percent via `Intl`), `$math` (add, subtract, multiply, divide, mod, min, max, round, floor, ceil, abs), `$concat`, `$count`, `$truncate`, `$pluralize`, and `$join`. Also exports `createI18nDirective` for `$t` translation keys with `{{param}}` interpolation, and `standardDirectives` for one-line registration (#279)
+
+### Improvements
+
+- **Example READMEs** — Added documentation to the chat, dashboard, game-engine, and no-ai examples (#277)
+
+### Contributors
+
+- @ctate
+
+
+## 0.18.0
+
+### New Features
+
- **Devtools** — Five new packages for inspecting json-render apps in the browser: `@json-render/devtools` (framework-agnostic core), plus `@json-render/devtools-react`, `@json-render/devtools-vue`, `@json-render/devtools-svelte`, and `@json-render/devtools-solid` adapters. Drop `` into your app to get a shadow-DOM-isolated panel with six tabs (Spec, State, Actions, Stream, Catalog, Pick), a DOM picker that maps clicked elements back to spec keys via `data-jr-key`, a capped event store, and server-side stream tap utilities. Floating toggle or `Cmd`/`Ctrl` + `Shift` + `J`, tree-shakes to `null` in production (#273)
- **Devtools example** — New `examples/devtools` Next.js demo showing the full devtools panel wired up to an AI chat endpoint and a component catalog (#273)
- **Action observer and devtools flag in core** — `@json-render/core` now exposes an action observer and a devtools enablement flag that adapters use to mirror actions and stream events into the panel (#273)
@@ -21,7 +37,6 @@
- @ctate
- @mvanhorn
-
## 0.17.0
diff --git a/apps/web/app/(main)/docs/changelog/page.mdx b/apps/web/app/(main)/docs/changelog/page.mdx
index da857b75..a5ca4e97 100644
--- a/apps/web/app/(main)/docs/changelog/page.mdx
+++ b/apps/web/app/(main)/docs/changelog/page.mdx
@@ -5,6 +5,35 @@ export const metadata = pageMetadata("docs/changelog")
Notable changes and updates to json-render.
+## v0.19.0
+
+May 6, 2026
+
+### New: Custom Directives API
+
+`@json-render/core` now supports custom directives via `defineDirective`, letting you declare new JSON shapes (like `$format`, `$math`) that resolve to computed values at render time. Directives compose naturally -- nest `$format` over `$math` over `$state` and they resolve inside-out. All four renderers (React, Vue, Svelte, Solid) have built-in directive resolution.
+
+### New: `@json-render/directives`
+
+New package shipping seven ready-made directives: `$format` (date, currency, number, percent via `Intl`), `$math` (arithmetic and rounding), `$concat`, `$count`, `$truncate`, `$pluralize`, and `$join`. Also exports `createI18nDirective` for `$t` translation keys with interpolation, and `standardDirectives` for one-line registration.
+
+```bash
+npm install @json-render/directives
+```
+
+```tsx
+import { standardDirectives } from "@json-render/directives";
+
+const catalog = createCatalog({
+ directives: standardDirectives,
+ // ...
+});
+```
+
+See the [Directives guide](/docs/directives) and the [API reference](/docs/api/directives) for details.
+
+---
+
## v0.18.0
April 17, 2026
diff --git a/packages/codegen/package.json b/packages/codegen/package.json
index 637bc131..956c0404 100644
--- a/packages/codegen/package.json
+++ b/packages/codegen/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/codegen",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "Utilities for generating code from json-render UI trees",
"keywords": [
diff --git a/packages/core/package.json b/packages/core/package.json
index e7bbd48f..979338b7 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/core",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "JSON becomes real things. Define your catalog, register your components, let AI generate.",
"keywords": [
diff --git a/packages/devtools-react/package.json b/packages/devtools-react/package.json
index 11782654..a6e62dee 100644
--- a/packages/devtools-react/package.json
+++ b/packages/devtools-react/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/devtools-react",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "React adapter for @json-render/devtools. Drop-in component.",
"keywords": [
diff --git a/packages/devtools-solid/package.json b/packages/devtools-solid/package.json
index d865f6d9..bcafb3df 100644
--- a/packages/devtools-solid/package.json
+++ b/packages/devtools-solid/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/devtools-solid",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "SolidJS adapter for @json-render/devtools. Drop-in component.",
"keywords": [
diff --git a/packages/devtools-svelte/package.json b/packages/devtools-svelte/package.json
index 1a7118ec..176e7e87 100644
--- a/packages/devtools-svelte/package.json
+++ b/packages/devtools-svelte/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/devtools-svelte",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "Svelte adapter for @json-render/devtools. Drop-in component.",
"keywords": [
diff --git a/packages/devtools-vue/package.json b/packages/devtools-vue/package.json
index 1a16e1e9..7891db3f 100644
--- a/packages/devtools-vue/package.json
+++ b/packages/devtools-vue/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/devtools-vue",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "Vue adapter for @json-render/devtools. Drop-in component.",
"keywords": [
diff --git a/packages/devtools/package.json b/packages/devtools/package.json
index 201ad617..1d49952a 100644
--- a/packages/devtools/package.json
+++ b/packages/devtools/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/devtools",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "Framework-agnostic devtools core for json-render: event store, panel UI, picker, stream taps.",
"keywords": [
diff --git a/packages/directives/package.json b/packages/directives/package.json
index 86a97a75..db354dd9 100644
--- a/packages/directives/package.json
+++ b/packages/directives/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/directives",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "Pre-built directives for @json-render/core — $format, $math, $concat, $count, $truncate, $pluralize, $join, and $t (i18n).",
"keywords": [
diff --git a/packages/image/package.json b/packages/image/package.json
index c7a0b3dc..791bd21c 100644
--- a/packages/image/package.json
+++ b/packages/image/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/image",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "Image renderer for @json-render/core. JSON becomes SVG and PNG images via Satori.",
"keywords": [
diff --git a/packages/ink/package.json b/packages/ink/package.json
index 955f5d74..dfe465aa 100644
--- a/packages/ink/package.json
+++ b/packages/ink/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/ink",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "Ink terminal renderer for @json-render/core. JSON becomes terminal UIs.",
"keywords": [
diff --git a/packages/jotai/package.json b/packages/jotai/package.json
index 90094f84..b2c1828c 100644
--- a/packages/jotai/package.json
+++ b/packages/jotai/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/jotai",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "Jotai adapter for json-render StateStore",
"keywords": [
diff --git a/packages/mcp/package.json b/packages/mcp/package.json
index 06590931..0aef4cd3 100644
--- a/packages/mcp/package.json
+++ b/packages/mcp/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/mcp",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "MCP Apps integration for @json-render/core. Serve json-render UIs as interactive MCP Apps in Claude, ChatGPT, Cursor, and VS Code.",
"keywords": [
diff --git a/packages/next/package.json b/packages/next/package.json
index 2492602c..298bf89b 100644
--- a/packages/next/package.json
+++ b/packages/next/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/next",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "Next.js renderer for @json-render/core. JSON becomes full Next.js applications with routes, layouts, metadata, and SSR.",
"keywords": [
diff --git a/packages/react-email/package.json b/packages/react-email/package.json
index c1bddd79..4f8355a8 100644
--- a/packages/react-email/package.json
+++ b/packages/react-email/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/react-email",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "React Email renderer for @json-render/core. JSON becomes HTML emails.",
"keywords": [
diff --git a/packages/react-native/package.json b/packages/react-native/package.json
index f4e26d23..ffcb46ac 100644
--- a/packages/react-native/package.json
+++ b/packages/react-native/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/react-native",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "React Native renderer for @json-render/core. JSON becomes React Native components.",
"keywords": [
diff --git a/packages/react-pdf/package.json b/packages/react-pdf/package.json
index f0819d36..a9b0b5ce 100644
--- a/packages/react-pdf/package.json
+++ b/packages/react-pdf/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/react-pdf",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "React PDF renderer for @json-render/core. JSON becomes PDF documents.",
"keywords": [
diff --git a/packages/react-three-fiber/package.json b/packages/react-three-fiber/package.json
index ecd05fd6..278aca0f 100644
--- a/packages/react-three-fiber/package.json
+++ b/packages/react-three-fiber/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/react-three-fiber",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "React Three Fiber renderer for @json-render/core. JSON becomes 3D scenes.",
"keywords": [
diff --git a/packages/react/package.json b/packages/react/package.json
index 80870be5..d64678f2 100644
--- a/packages/react/package.json
+++ b/packages/react/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/react",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "React renderer for @json-render/core. JSON becomes React components.",
"keywords": [
diff --git a/packages/redux/package.json b/packages/redux/package.json
index 75d332b0..f27edf85 100644
--- a/packages/redux/package.json
+++ b/packages/redux/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/redux",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "Redux adapter for json-render StateStore",
"keywords": [
diff --git a/packages/remotion/package.json b/packages/remotion/package.json
index 5b28a5f7..85d9abf3 100644
--- a/packages/remotion/package.json
+++ b/packages/remotion/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/remotion",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "Remotion renderer for @json-render/core. JSON becomes video compositions.",
"keywords": [
diff --git a/packages/shadcn-svelte/package.json b/packages/shadcn-svelte/package.json
index 61ed693e..122718b2 100644
--- a/packages/shadcn-svelte/package.json
+++ b/packages/shadcn-svelte/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/shadcn-svelte",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "shadcn-svelte component library for @json-render/svelte. JSON becomes beautiful Tailwind-styled Svelte components.",
"keywords": [
diff --git a/packages/shadcn/package.json b/packages/shadcn/package.json
index 78094dd7..18569b07 100644
--- a/packages/shadcn/package.json
+++ b/packages/shadcn/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/shadcn",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "shadcn/ui component library for @json-render/core. JSON becomes beautiful Tailwind-styled React components.",
"keywords": [
diff --git a/packages/solid/package.json b/packages/solid/package.json
index e0edc025..12ebe16c 100644
--- a/packages/solid/package.json
+++ b/packages/solid/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/solid",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "SolidJS renderer for @json-render/core. JSON becomes Solid components.",
"keywords": [
diff --git a/packages/svelte/package.json b/packages/svelte/package.json
index 43529f69..c64549d7 100644
--- a/packages/svelte/package.json
+++ b/packages/svelte/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/svelte",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "Svelte 5 renderer for @json-render/core. JSON becomes Svelte components.",
"keywords": [
diff --git a/packages/vue/package.json b/packages/vue/package.json
index 59773146..608741a1 100644
--- a/packages/vue/package.json
+++ b/packages/vue/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/vue",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "Vue renderer for @json-render/core. JSON becomes Vue components.",
"keywords": [
diff --git a/packages/xstate/package.json b/packages/xstate/package.json
index 33f233b5..8e5e631c 100644
--- a/packages/xstate/package.json
+++ b/packages/xstate/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/xstate",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "XState Store adapter for json-render StateStore",
"keywords": [
diff --git a/packages/yaml/package.json b/packages/yaml/package.json
index 45fe2a35..7fafb4eb 100644
--- a/packages/yaml/package.json
+++ b/packages/yaml/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/yaml",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "YAML wire format for @json-render/core. Progressive rendering and surgical edits via streaming YAML.",
"keywords": [
diff --git a/packages/zustand/package.json b/packages/zustand/package.json
index 49d5cab1..bf33f826 100644
--- a/packages/zustand/package.json
+++ b/packages/zustand/package.json
@@ -1,6 +1,6 @@
{
"name": "@json-render/zustand",
- "version": "0.18.0",
+ "version": "0.19.0",
"license": "Apache-2.0",
"description": "Zustand adapter for json-render StateStore",
"keywords": [