Riadh Chelbi
← All posts

Expressive Code Feature Showcase

Date —
July 20, 2026
Tags —
Astro, Markdown, Code

This post demonstrates every way you can use Expressive Code in your Markdown. No plugins required — everything here works with the default astro-expressive-code installation.

Syntax Highlighting

Any fenced code block with a language identifier gets syntax highlighting. Over 100 languages are supported out of the box.

function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
console.log(fibonacci(10)); // 55
interface BlogPost {
title: string;
summary: string;
date: Date;
tags: string[];
draft: boolean;
}
function isPublished(post: BlogPost): boolean {
return !post.draft && post.date <= new Date();
}
.prose {
--tw-prose-body: var(--color-ink-soft);
--tw-prose-headings: var(--color-ink);
--tw-prose-links: var(--color-signal);
}
.prose :where(h2, h3, h4) {
font-family: var(--font-display);
}
<header class="sticky top-0 z-40 border-b backdrop-blur">
<nav aria-label="Primary">
<a href="/work" aria-current="page">Work</a>
<a href="/blog">Blog</a>
</nav>
</header>

Editor Frames

Add a title attribute to show an editor-style frame with a file tab.

src/utils/formatDate.ts
export function formatDate(date: Date, style = 'long') {
return new Intl.DateTimeFormat('en', {
dateStyle: style,
timeZone: 'UTC',
}).format(date);
}
src/pages/about.astro
---
import BaseLayout from '@/layouts/BaseLayout.astro';
---
<BaseLayout title="About">
<h1>About me</h1>
</BaseLayout>
package.json
{
"name": "astro-starter-portfolio",
"type": "module",
"scripts": {
"dev": "astro dev",
"build": "astro build"
}
}

Terminal Frames

Shell languages (bash, sh, zsh, shell) automatically get a terminal-style frame.

Terminal window
pnpm add astro-expressive-code
pnpm astro build

Deploy to Vercel
pnpm add -g vercel
vercel --prod

Terminal window
cd ~/projects/astro-starter-portfolio
pnpm run dev

File Name Comments

If no title is set, Expressive Code extracts a file name from a comment in the first 4 lines.

src/components/Button.astro
const href = Astro.props.href;
const variant = Astro.props.variant ?? 'solid';
src/styles/global.css
:root {
--paper: #f7f6f3;
--ink: #15161b;
}
src/layouts/BaseLayout.astro
<html lang="en">
<body>
<slot />
</body>
</html>

Frame Type Overrides

Force a specific frame type with the frame attribute.

No frame

Use frame="none" to strip the frame entirely — useful for short inline snippets.

const x = 42;

Force code frame

Use frame="code" to get an editor frame even without a title.

echo "no frame at all"

Force terminal frame

Use frame="terminal" to get a terminal frame for non-shell languages.

Terminal window
node --version

Text Markers

Highlight specific lines using the [lineNumbers] meta syntax.

Single line

const SITE = {
name: 'Riadh Chelbi',
role: 'System Engineer',
};

Multiple lines

import { getCollection } from 'astro:content';
const allWork = await getCollection('work');
const sorted = allWork.sort((a, b) => b.date - a.date);
const featured = sorted.filter((e) => e.data.featured);
const rest = sorted.filter((e) => !e.data.featured);
const selected = [...featured, ...rest].slice(0, 5);

Diff-style markers

Use + for additions and - for removals.

const a = 'hello';
const b = 'world';
const greeting = `${a} ${b}`;
const target = greeting;
console.log(target);
// console.log('done');

ANSI Escape Sequences

Set the language to ansi to render terminal output with color.

Terminal window
[1;4mStyles available:[0m
[2m- Dimmed[0m
[1m- Bold[0m
[3m- Italic[0m
[4m- Underline[0m
[9m- Strikethrough[0m
[1;4mColors:[0m
[31m Red [32m Green [33m Yellow [34m Blue [35m Magenta [36m Cyan [0m

Multiple Languages

Mix languages in a single post to show different highlighting styles.

astro.config.mjs (YAML-style config)
integrations:
- name: sitemap
- name: astro-expressive-code
themes:
- github-dark-default
- github-light-default
pyproject.toml
[tool.ruff]
line-length = 100
target-version = "py312"
[tool.ruff.lint]
select = ["E", "F", "I"]
SELECT posts.title, posts.date, COUNT(comments.id) AS comment_count
FROM posts
LEFT JOIN comments ON comments.post_id = posts.id
WHERE posts.draft = false
GROUP BY posts.id
ORDER BY posts.date DESC
LIMIT 10;

Nested Code Blocks

You can show Markdown that contains code fences by using extra backticks.

// This is a regular code block
const x = 1;
const std = @import("std");
fn main() {
std.debug.print("Hello, World!");
}