Build a Mini Converter Tool in SvelteKit (100% Static, Client-Side Only)
JSON-to-YAML converter as a prerendered page with csr:true. No API routes, works on GitHub Pages.
Tested on: SvelteKit 2.16, js-yaml 4.1
Goal: /tools/json-to-yaml — paste JSON, get YAML. Zero server.
Route setup
src/routes/tools/json-to-yaml/
├── +page.svelte
└── +page.ts
+page.ts:
export const prerender = true;
export const csr = true; // needs interactivity
The page
<script lang="ts">
let input = $state('{"hello": "world"}');
let output = $state('');
let error = $state('');
async function convert() {
error = '';
output = '';
try {
const obj = JSON.parse(input);
const yaml = await import('js-yaml');
output = yaml.dump(obj, { indent: 2, lineWidth: 80 });
} catch (e) {
error = e instanceof Error ? e.message : 'Invalid JSON';
}
}
</script>
<svelte:head>
<title>JSON to YAML Converter</title>
<meta name="description" content="Free client-side JSON to YAML converter." />
</svelte:head>
<h1>JSON → YAML</h1>
<textarea bind:value={input} rows="12" class="w-full font-mono"></textarea>
<button onclick={convert}>Convert</button>
{#if error}
<p class="text-red-600">{error}</p>
{/if}
{#if output}
<pre class="bg-gray-100 p-4 rounded"><code>{output}</code></pre>
<button onclick={() => navigator.clipboard.writeText(output)}>Copy</button>
{/if}
Error — yaml in server bundle
'js-yaml' is imported by ... but could not be resolved during SSR
Dynamic import inside the click handler (shown above) keeps it client-only.
SEO for tool pages
Add structured data in +page.svelte:
<svelte:head>
{@html `<script type="application/ld+json">${JSON.stringify({
'@context': 'https://schema.org',
'@type': 'WebApplication',
name: 'JSON to YAML Converter',
applicationCategory: 'DeveloperApplication',
offers: { '@type': 'Offer', price: '0' }
})}</script>`}
</svelte:head>
Shared tools layout
src/routes/tools/+layout.svelte:
<script lang="ts">
let { children } = $props();
</script>
<nav>
<a href="/tools/json-to-yaml/">JSON→YAML</a>
<a href="/tools/base64/">Base64</a>
</nav>
{@render children()}
src/routes/tools/+layout.ts:
export const prerender = true;
export const csr = true;
Build output check
npm run build
ls build/tools/json-to-yaml/
# index.html + _app/ chunks with js-yaml lazy chunk
Real user error handling
JSON.parse: Unexpected token } in JSON at position 47
Show the raw message — developers need line-level feedback, not "Something went wrong."
Metrics after deploy
Page weight: 12 KB HTML + 48 KB JS (on first convert click)
Lighthouse Performance: 96
Works on Cloudflare Pages, Netlify, GitHub Pages
Zero server cost per conversion