A drop-in browser script that automatically syntax-highlights <pre><code> blocks using Shiki. Zero dependencies, zero build steps for consumers, framework-agnostic.
- Automatic detection and highlighting of all
<pre><code>blocks on the page - 60+ bundled themes with automatic dark mode support via
prefers-color-scheme - All Shiki bundled languages supported out of the box
- Language label and copy-to-clipboard button on every code block
- Self-contained single script — CSS is embedded, Shiki is loaded from esm.sh at runtime
- Works with any static site, CMS, blog, or HTML page
Add a single <script> tag to your HTML:
<script src="https://cdn.jsdelivr.net/gh/vespaiach/shiki-loader@main/public/shiki-loader.js" defer></script>That's it. All code blocks with a language class will be highlighted on page load.
<!-- Single theme -->
<script src="https://cdn.jsdelivr.net/gh/vespaiach/shiki-loader@main/public/shiki-loader.js?theme=github-dark" defer></script>
<!-- Light and dark mode themes -->
<script
src="https://cdn.jsdelivr.net/gh/vespaiach/shiki-loader@main/public/shiki-loader.js?theme=github-light&dark-theme=github-dark"
defer
></script>The loader highlights any <code> element inside a <pre> that has a language-* or lang-* class:
<pre><code class="language-typescript">
const greeting: string = 'Hello, world!';
console.log(greeting);
</code></pre>The loader is configured entirely via URL query parameters on the script src.
| Parameter | Values | Default | Description |
|---|---|---|---|
theme |
Any bundled Shiki theme name | material-theme |
Theme used in light mode (and dark if no dark-theme) |
dark-theme |
Any bundled Shiki theme name | (none) | Theme used when system prefers dark mode |
Dark mode is detected automatically via prefers-color-scheme: dark. When dark-theme is set and the system is in dark mode, that theme is used; otherwise theme applies.
Popular themes include:
| Light | Dark |
|---|---|
github-light |
github-dark |
one-light |
one-dark-pro |
catppuccin-latte |
catppuccin-mocha |
material-theme-lighter |
material-theme-darker |
rose-pine-dawn |
rose-pine |
solarized-light |
solarized-dark |
everforest-light |
everforest-dark |
min-light |
nord |
See src/shiki-loader/themes.ts for the full list of 60+ themes.
Build the loader locally and serve shiki-loader.js from your own domain:
git clone https://github.com/vespaiach/shiki-loader.git
cd shiki-loader
bun install
bun run build:libThe output file is public/shiki-loader.js. Copy it to your project's static assets and reference it in your HTML:
<script src="/assets/shiki-loader.js?theme=dracula" defer></script>- On DOM ready, the script reads
themeanddark-themefrom its own URL parameters - Detects system color scheme preference
- Finds all
<pre><code>elements with alanguage-*orlang-*class - Highlights each block using Shiki (loaded from esm.sh at runtime)
- Wraps each block with a language label and copy-to-clipboard button
- Injects embedded CSS for styling
The script includes a single-run guard — it is safe to inject multiple times and will only highlight once.
- Bun 1.0+
bun install
bun run type-check # Verify TypeScript setup
bun run build:lib # Build the loader| Command | Purpose |
|---|---|
bun run dev |
Start Next.js dev server at localhost:3000 |
bun run build:lib |
Build standalone loader to public/shiki-loader.js |
bun run build |
Build Next.js static site |
bun run start |
Run production server |
bun run lint |
Lint with Biome |
bun run format |
Format with Biome |
bun run type-check |
TypeScript type-check (no emit) |
bun test |
Run tests |
src/
├── shiki-loader/ # The standalone loader (framework-agnostic)
│ ├── index.ts # Entry point — DOM detection and highlighting
│ ├── transformer.ts # Wraps highlighted blocks with UI (label + copy button)
│ ├── utils.ts # URL param parsing, CSS injection, clipboard handling
│ ├── loader.css # Styles embedded into the bundle at build time
│ └── themes.ts # List of supported Shiki themes
├── app/ # Next.js demo app
│ ├── layout.tsx # Root layout (Tailwind + DaisyUI)
│ ├── page.tsx # Home page
│ ├── about/page.tsx # About page
│ └── _components/ # Demo-specific components
└── components/ # Shared UI components (Select, icons)
bun run build:lib does:
- Bundles
src/shiki-loader/index.tswith Bun (--target browser --bundle --minify) - Runs
scripts/embed-loader-css.tsto inlineloader.cssinto the JS bundle - Outputs final self-contained script to
public/shiki-loader.js
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-feature) - Run checks before committing:
bun run lint bun run type-check bun run build:lib bun run build
- Open a pull request
MIT - Trinh Nguyen