Skip to content

Tạo Custom MDX Components với Svelte trong Starlight

Tạo Custom MDX Components với Svelte trong Starlight

Section titled “Tạo Custom MDX Components với Svelte trong Starlight”

Bạn đã thêm Svelte vào Starlight nhưng không biết cách tạo custom components như:

  • :::code - Code blocks với filename, syntax highlighting
  • :::terminal - Terminal output với copy button
  • :::alert - Thông báo alerts với nhiều types
  • :::checklist - Checklist có thể tích
  • :::tabs - Code tabs cho nhiều languages

Bước 1: Cài đặt Svelte trong Starlight

Section titled “Bước 1: Cài đặt Svelte trong Starlight”

Đã có sẵn trong project! Kiểm tra:

astro.config.mjs
import svelte from '@astrojs/svelte';
export default defineConfig({
integrations: [svelte()],
});

Tạo các Svelte components trong src/components/:

src/components/Alert.astro
---
interface Props {
type?: 'info' | 'warning' | 'success' | 'error';
title?: string;
}
const { type = 'info', title } = Astro.props;
const icons = {
info: 'ℹ️',
warning: '⚠️',
success: '',
error: ''
};
const colors = {
info: '#3b82f6',
warning: '#f59e0b',
success: '#10b981',
error: '#ef4444'
};
---
<div class="alert" data-type={type}>
<div class="alert-header">
<span class="icon">{icons[type]}</span>
{title && <span class="title">{title}</span>}
</div>
<div class="alert-content">
<slot />
</div>
</div>
<style define:vars={{ color: colors[type] }}>
.alert {
border-left: 4px solid var(--color);
background: rgba(var(--color-rgb), 0.1);
border-radius: 8px;
padding: 1rem;
margin: 1rem 0;
}
.alert-header {
display: flex;
align-items: center;
gap: 0.5rem;
font-weight: 600;
margin-bottom: 0.5rem;
}
</style>
src/components/Terminal.svelte
<script>
export let title = 'Terminal';
let copied = false;
function copyCode() {
const code = document.querySelector('.terminal-content code')?.textContent || '';
navigator.clipboard.writeText(code);
copied = true;
setTimeout(() => copied = false, 2000);
}
</script>
<div class="terminal">
<div class="terminal-header">
<span class="terminal-title">{title}</span>
<button class="copy-btn" on:click={copyCode}>
{copied ? '✅ Copied!' : '📋 Copy'}
</button>
</div>
<div class="terminal-content">
<slot />
</div>
</div>
<style>
.terminal {
background: #1e1e1e;
border-radius: 8px;
overflow: hidden;
margin: 1rem 0;
}
.terminal-header {
background: #2d2d2d;
padding: 0.5rem 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.terminal-content {
padding: 1rem;
color: #d4d4d4;
font-family: 'Fira Code', monospace;
overflow-x: auto;
}
.copy-btn {
background: transparent;
border: 1px solid #555;
color: #aaa;
padding: 0.25rem 0.5rem;
border-radius: 4px;
cursor: pointer;
font-size: 0.75rem;
}
.copy-btn:hover {
background: #444;
color: white;
}
</style>
src/components/CodeBlock.svelte
<script>
export let language = 'javascript';
export let filename = '';
export let highlightLines = '';
export let lineNumbers = false;
export let copyButton = true;
let copied = false;
function copyCode() {
const code = document.querySelector('.code-content')?.textContent || '';
navigator.clipboard.writeText(code);
copied = true;
setTimeout(() => copied = false, 2000);
}
</script>
<div class="code-block">
{#if filename || copyButton}
<div class="code-header">
{#if filename}
<span class="filename">{filename}</span>
{/if}
{#if copyButton}
<button class="copy-btn" on:click={copyCode}>
{copied ? '' : ''}
</button>
{/if}
</div>
{/if}
<pre class="code-content" class:line-numbers={lineNumbers}><code class="language-{language}"><slot /></code></pre>
</div>
<style>
.code-block {
background: #1e1e1e;
border-radius: 8px;
overflow: hidden;
margin: 1rem 0;
}
.code-header {
background: #2d2d2d;
padding: 0.5rem 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.filename {
color: #888;
font-size: 0.875rem;
}
.copy-btn {
background: transparent;
border: none;
color: #888;
cursor: pointer;
font-size: 1rem;
}
.code-content {
padding: 1rem;
margin: 0;
overflow-x: auto;
color: #d4d4d4;
}
</style>

Bước 3: Đăng ký Components trong Starlight

Section titled “Bước 3: Đăng ký Components trong Starlight”

Cập nhật astro.config.mjs:

import starlight from '@astrojs/starlight';
import { defineConfig } from 'astro/config';
export default defineConfig({
integrations: [
starlight({
title: 'My Docs',
components: {
// Custom Alert
'Aside': './src/components/Alert.astro',
},
}),
],
});
import Alert from '../../components/Alert.astro';
import Terminal from '../../components/Terminal.svelte';
import CodeBlock from '../../components/CodeBlock.svelte';
# Bài viết
<Alert type="info" title="Lưu ý">
Đây là thông tin quan trọng!
</Alert>
<Terminal title="Cài đặt">
$ npm install astro
✓ Installed 1 package
</Terminal>
<CodeBlock language="javascript" filename="config.js">
export default defineConfig({
site: 'https://example.com'
});
</CodeBlock>

Starlight Built-in Components (Miễn phí!)

Section titled “Starlight Built-in Components (Miễn phí!)”

Starlight có sẵn nhiều components:

ComponentCách dùngMô tả
Aside<aside type="note">Thay thế alert
Steps<Steps>{...}</Steps>Numbered steps
Tabs<TabItem>Code/language tabs
FileTree<FileTree>File directory structure
import { Aside } from '@astrojs/starlight/components';
<Aside type="note">
Đây là thông tin!
</Aside>
<Aside type="tip">
Mẹo hữu ích
</Aside>
<Aside type="caution">
Cần cẩn thận
</Aside>
<Aside type="danger">
Nguy hiểm!
</Aside>
import { Steps } from '@astrojs/starlight/components';
<Steps>
1. Cài đặt Astro
2. Tạo project
3. Chạy dev server
</Steps>
  • ✅ Svelte tích hợp sẵn trong Starlight
  • ✅ Tạo custom components theo nhu cầu
  • ✅ Starlight có sẵn Aside, Steps, Tabs, FileTree
  • ⚠️ Không dùng :::syntax - cần đăng ký components

Tài liệu tham khảo: