SvelteKit Local JSON Data: No Backend, No CMS, No Regrets
Load blog posts and tool configs from JSON files at build time using import.meta.glob. Includes TypeScript types and hot reload.
Tested on: SvelteKit 2.16, TypeScript 5.7 Use case: 40 blog posts, 12 tool configs. No headless CMS budget.
Folder layout
src/
├── lib/data/
│ ├── posts.json
│ ├── tools.json
│ └── types.ts
└── routes/
├── blog/+page.ts
└── blog/[slug]/+page.ts
types.ts
export interface Post {
slug: string;
title: string;
date: string;
tags: string[];
excerpt: string;
}
export interface PostDetail extends Post {
content: string;
}
posts.json (snippet)
[
{
"slug": "first-post",
"title": "First Post",
"date": "2026-05-01",
"tags": ["sveltekit"],
"excerpt": "Short summary.",
"content": "# Hello\n\nBody here."
}
]
Load at build time — blog index
src/routes/blog/+page.ts:
import type { PageLoad } from './$types';
import type { Post } from '$lib/data/types';
import posts from '$lib/data/posts.json';
export const prerender = true;
export const load: PageLoad = () => {
const sorted = (posts as Post[]).sort(
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
);
return { posts: sorted };
};
Dynamic slug route
src/routes/blog/[slug]/+page.ts:
import type { PageLoad } from './$types';
import type { PostDetail } from '$lib/data/types';
import posts from '$lib/data/posts.json';
export const prerender = true;
export const load: PageLoad = ({ params }) => {
const post = (posts as PostDetail[]).find((p) => p.slug === params.slug);
if (!post) throw error(404, 'Post not found');
return { post };
};
export function entries() {
return (posts as PostDetail[]).map((p) => ({ slug: p.slug }));
}
Alternative: glob many small JSON files
For one-file-per-post:
const modules = import.meta.glob('/src/content/posts/*.json', { eager: true });
const posts = Object.values(modules).map((m: any) => m.default);
Error — JSON import type complaints
Module '$lib/data/posts.json' cannot be imported because
it is not a valid ECMAScript module
tsconfig.json:
{
"compilerOptions": {
"resolveJsonModule": true,
"esModuleInterop": true
}
}
Hot reload works
Edit posts.json, save — Vite HMR refreshes the page in dev. No API restart.
When this breaks down
Past ~200 posts, consider MDX or a CMS. JSON in git gets noisy in diffs. For small sites, it's perfect.
Build time with 40 posts: 8s
Zero API calls at runtime
Zero hosting cost for data layer