loading code fetching the highlighted snippet
Map ShikiCode to the code renderer and inject a highlighter via Svelte context. Highlighting runs synchronously, so streaming stays enabled — edit the markdown live.
ShikiCode is a drop-in for the built-in code renderer
(same lang / text props) — only fenced blocks change.createHighlighterCoreSync + the JS regex engine highlight
synchronously, so the async-extension guard never trips and streaming stays
on.shiki/langs/* and shiki/themes/* you need —
everything else is tree-shaken out, and the core bundle stays shiki-free.Fenced blocks render through Shiki synchronously — no async work, so streaming stays enabled.
interface User {
id: number
name: string
}
const greet = (user: User): string => `Hello, ${user.name}!`{ "langs": ["javascript", "typescript", "json"], "theme": "github-dark" }Inline code uses the codespan renderer and is left untouched.
Unregistered languages (and any per-block failure) degrade to an escaped <pre class="shiki-fallback"> instead of throwing mid-stream — the untrusted lang is only ever an escaped data-lang.
lang is untrusted (agent/LLM input) — the fallback escapes, never interpolates it, emitting it only as an escaped data-lang.{@html} sink only ever receives library-generated or escaped markup..shiki-fallback to match your registered blocks — the content is
a plain escaped <pre><code>.### Registered language (`ts`)
```ts
const total: number = [1, 2, 3].reduce((a, b) => a + b, 0)
```
### Unregistered language (`rust`) — safe escaped fallback
```rust
fn main() {
let markup = "<b>bold</b> & <i>italic</i>";
println!("{markup}");
}
```ts)const total: number = [1, 2, 3].reduce((a, b) => a + b, 0)rust) — safe escaped fallbackfn main() {
let markup = "<b>bold</b> & <i>italic</i>";
println!("{markup}");
}