Codeblocks
This repository shows how to implement reusable Codeblock components inside of a SvelteKit project, with syntax highlighting by Shiki. This is an alternative to the more common approach with markdown.
Install / Use
/learn @ScriptRaccoon/CodeblocksREADME
Codeblock Components with Shiki in SvelteKit
This repository shows how to implement reusable Codeblock components inside of a SvelteKit project. Syntax highlighting is implemented via Shiki.
This is an alternative to the more common approach with markdown (see for example SvelteKit Shiki Syntax Highlighting: Markdown Code Blocks by Rodney Johnson).
Demo: https://codeblocks-shiki.netlify.app/
Snippets
The snippets are located in the folder $lib/snippets. This folder can be changed. For example:
/* $lib/snippets/style.css */
.section {
padding-block: 1rem;
color: #222;
}
Saving each snippet, even when it is just one line, in a separate file may sound overkill, but this is necessary for the method presented here, and it also has the advantage that your editor does the code formatting for you. (Shiki only highlights the code.)
Shiki code
We install Shiki with npm i shiki.
The file codes.ts exports a function which uses Shiki to compute an object with all HTML codes of the snippets. The keys are the file names. Here you can also adjust the supported languages and themes as well as the snippet path folder (this has to be a string literal, hence we cannot make it into a variable).
// $lib/server/codes.ts
import { getHighlighter } from "shiki";
export async function compute_codes() {
const highlighter = await getHighlighter({
theme: "dark-plus",
langs: ["html", "js", "css", "svelte"],
});
const snippets = import.meta.glob("$lib/snippets/*", {
as: "raw",
eager: true,
});
const codes = Object.fromEntries(Object.entries(snippets).map(transform));
function transform([path, file_content]: [string, string]) {
const file_name = path.split("/").at(-1)!;
const lang = file_name.split(".").at(-1);
const code = highlighter.codeToHtml(file_content, { lang });
return [file_name, code];
}
return codes;
}
To explain this a little bit, notice that Vite's import.meta.glob returns an object whose keys are the file paths and whose values are the file contents. In the transform function, we let Shiki operate on the file content and replace the file path by the file name. Shiki needs the language, which we can extract from the file extension.
Page Data
The layout server load uses this function to make the codes available as page data.
// +layout.server.ts
export const prerender = true;
import { compute_codes } from "$lib/server/codes";
export const load = async () => {
const codes = await compute_codes();
return { codes };
};
Notice that pages with code blocks need to be prerendered, and Shiki needs to run on the server only. Otherwise there will be an error. This is why we set prerender = true here.
When the code blocks are located only on a single page, you can also use a page server load instead.
Codeblock component
These codes are then used in the Codeblock.svelte component. It exports a prop snippet and computes the rendered code via codes[snippet].
<!-- $lib/components/Codeblock.svelte -->
<script lang="ts">
import { page } from "$app/stores";
export let snippet = "";
const code = $page.data.codes?.[snippet];
</script>
{#if code}
<div>{@html code}</div>
{/if}
This produces rather crude-looking code blocks, though. You can improve the styling here as follows.
div :global(pre) {
font-size: 1rem;
padding: 1.25rem;
border-radius: 0.5rem;
margin-block: 1rem;
overflow: auto;
}
The last property is important, since it adds scrollbars when the code is too wide. We attach a padding to the pre element generated by Shiki in order to keep the background color determined by the theme.
When an invalid snippet is passed to the component, nothing is rendered. In order to catch bugs during development, you can expand the if-block as follows:
{:else}
<div><strong>Invalid code snippet: {snippet}</strong></div>
{/if}
Using the component
The Codeblock.svelte component accepts the file name of one of these snippets as a prop.
<!-- +page.svelte -->
<Codeblock snippet="index.html" />
<Codeblock snippet="script.js" />
<Codeblock snippet="style.css" />
<Codeblock snippet="Counter.svelte" />
Acknowledgements
Thanks to karimfromjordan and Patrick on the Svelte Discord server for their help with the implementation.
Related Skills
node-connect
347.6kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
108.4kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
347.6kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
347.6kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
