November 11, 2024
Create Blog with SvelteKit
If youโve ever wanted to create a blog with SvelteKit, youโve come to the right place.
Thereโs two pieces you need: mdsvex
and a simple function to generate a list of blog posts.
Setup mdsvex
To install mdsvex
, run the following command:
bun i -D mdsvex
Then add it to svelte.config.js
file:
import { mdsvex } from "mdsvex";
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: [
mdsvex({
extensions: [".md"],
layout: {
_: "./src/routes/blog/Layout.svelte",
},
}),
],
extensions: [".svelte", ".md"],
};
If you have a specific layout you want to use for each blog post, create a Layout.svelte
file in the src/routes/blog
directory.
Then you can get the frontmatter as props in your layout like this:
<script lang="ts">
import { type Snippet } from "svelte";
// Props
const {
title,
description,
children,
}: {
title: string;
description: string;
children: Snippet;
} = $props();
</script>
<svelte:head>
<title>{title} - SvelteRust</title>
<meta name="description" content="{description}" />
</svelte:head>
<article>
<h1>{title}</h1>
{@render children()}
</article>
So when you have a create a blog post like this, the frontmatter will be available as props in Layout.svelte
:
---
title: Create Blog with SvelteKit
description: Learn the easiest way to create a blog with SvelteKit.
tags: sveltekit, blog
---
If you've ever wanted to create a blog with SvelteKit, you've come to the right place.
There's two pieces you need: `mdsvex` and a simple function to generate a list of blog posts.
Generate a list of blog posts automatically
To generate a list of blog posts automatically, you can use following +page.server.ts
file which you put in the src/routes/blog
directory:
import fs from "fs";
import type { PageServerLoad } from "./$types";
type Markdown = {
metadata: {
title: string;
description: string;
tags: string;
};
};
export const load: PageServerLoad = async () => {
// Get all articles with metadata
const articles = Object.entries(import.meta.glob<Markdown>("/src/routes/blog/**/*.md", { eager: true })).map(([path, { metadata }]) => {
const stats = fs.statSync(`.${path}`);
const url = path.replace("/src/routes/blog/", "").replace("/+page.md", "");
const tags = metadata.tags.split(",").map((tag) => tag.trim());
return { ...metadata, url, tags, created: stats.birthtime };
});
articles.sort((a, b) => b.created.getTime() - a.created.getTime());
return { articles };
};
Then you can render each article nicely in +page.svelte
, same directory as the above +page.server.ts
file:
<script lang="ts">
import { Badge } from "$ui/badge";
// Props
const { data } = $props();
const { articles } = $derived(data);
</script>
<svelte:head>
<title>Blog - SvelteRust</title>
<meta
name="description"
content="Explore the SvelteRust blog for tutorials and tips on SvelteKit and Rust. Stay updated with the latest in programming insights."
/>
</svelte:head>
<h1 class="h2">Blog</h1>
<p class="p mt-4">
Check out the latest articles from SvelteRust below. The articles are mostly tutorials made for SvelteKit and Rust, but also contain tips
and tricks.
</p>
<div class="mt-6 grid gap-4">
{#each articles as article}
<a href="/blog/{article.url}" class="hover:bg-muted rounded border p-4 duration-100">
<p class="p !text-slate-500">
{new Date(article.created).toLocaleDateString("en-US", { month: "long", day: "numeric", year: "numeric" })}
</p>
<h2 class="h3">{article.title}</h2>
<p class="p mt-2">{article.description}</p>
{#if article.tags && article.tags.length > 0}
<div class="mt-2 flex gap-2">
{#each article.tags as tag}
<Badge class="text-md">{tag}</Badge>
{/each}
</div>
{/if}
</a>
{/each}
</div>
Your blog directory should look like this. Just create a new folder with the slug/url you want, and create a +page.md
file inside it:
blog
โโโ create-blog-with-sveltekit
โย ย โโโ +page.md
โโโ Layout.svelte
โโโ +page.server.ts
โโโ +page.svelte
And thatโs all you need to create a simple blog with SvelteKit that is automatically generated from your markdown files.
BONUS: Generate RSS Feed
To generate a RSS feed, move the logic for getting articles into src/lib/articles.ts
, then add the following to src/routes/rss/+server.ts
:
import { getArticles } from "$lib/articles";
export async function GET() {
const articles = getArticles();
const xml = `
<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>SvelteRust</title>
<link>https://svelterust.com</link>
<description>Articles about tutorials made for SvelteKit and Rust, as well as tips and tricks.</description>
<atom:link href="https://svelterust.com/rss" rel="self" type="application/rss+xml" />
${articles
.map(
(article) => `
<item>
<title>${article.title}</title>
<link>https://svelterust.com/blog/${article.url}</link>
<description>${article.description}</description>
<pubDate>${article.created.toUTCString()}</pubDate>
<guid>https://svelterust.com/blog/${article.url}</guid>
</item>
`,
)
.join("")}
</channel>
</rss>
`.trim();
return new Response(xml, {
headers: {
"Content-Type": "application/xml",
},
});
}