## Demo: portable-text ### PortableTextDemo.svelte ```svelte

Portable Text stores rich text as an array of JSON blocks instead of HTML, so the same content can render to a web page, a PDF or a native app. @portabletext/svelte is the official Svelte 5 renderer for it; the format itself is documented at portabletext.org. Everything on this page is server-rendered by Mochi except the last section.

Install

The same renderer, hydrated and not

Both panels below are the same component rendered from the same JSON. The left one is plain SSR markup; the right one carries mochi:hydrate, so the renderer ships to the browser and re-runs on every keystroke.

Hydration is all-or-nothing per island: the right panel ships @portabletext/svelte plus every component it references, and its props are serialized in plain text into the page. The left panel ships nothing.
``` ### blocks.ts ```ts import type { InputValue } from '@portabletext/svelte'; // Every block and span carries an explicit _key: the renderer mints Math.random() keys for // missing ones, which would differ between the server render and the hydrated one. export const playground = [ { _type: 'block', _key: 'pg1', style: 'h3', children: [{ _type: 'span', _key: 'pg1s1', text: 'A rendered heading' }], }, { _type: 'block', _key: 'pg2', style: 'normal', children: [ { _type: 'span', _key: 'pg2s1', text: 'A paragraph with a ' }, { _type: 'span', _key: 'pg2s2', text: 'highlighted', marks: ['highlight'] }, { _type: 'span', _key: 'pg2s3', text: ' word.' }, ], }, { _type: 'callout', _key: 'pg3', text: 'And a custom callout block.' }, ] satisfies InputValue; export const playgroundJson = JSON.stringify(playground, null, 2); ``` ### CalloutBlock.svelte ```svelte {#if isInline} {:else} {/if} ``` ### Highlight.svelte ```svelte {@render children()} ``` ### Playground.svelte ```svelte
{live ? 'mochi:hydrate' : 'SSR only'} {live ? 'edit the JSON — the output re-renders' : 'no JavaScript shipped — the field is read-only'}
{#if parsed.error}

{parsed.error}

{/if}
``` ### routes.ts ```ts import { Mochi } from 'mochi-framework'; import type { MochiRouteValue } from 'mochi-framework'; export const routes: Record = { '/demos/portable-text': Mochi.page('./src/demos/portable-text/PortableTextDemo.svelte'), }; ``` ### index.ts ```ts import { Mochi, logger } from 'mochi-framework'; import { routes } from './routes'; await Mochi.serve({ port: 3333, development: process.env.MODE === 'development', routes, }); logger.info('Server running at http://localhost:3333'); ```