Skip to main content
Glama
CodeBlockShiki.tsx4.15 kB
'use client'; import { type FC, useEffect, useState } from 'react'; import type { BundledLanguage, BundledTheme, CodeToHastOptions, } from 'shiki/bundle/web'; import { CodeDefault } from './CodeBlockClient'; // Map of loaded modules to avoid re-importing const languageCache = new Map<BundledLanguage, any>(); const themeCache = new Map<BundledTheme, any>(); // Lazy load language modules const loadLanguage = async (lang: BundledLanguage): Promise<any> => { if (languageCache.has(lang)) { return languageCache.get(lang); } let languageModule: any; switch (lang) { case 'typescript': case 'ts': languageModule = await import('shiki/langs/typescript.mjs'); break; case 'javascript': case 'js': languageModule = await import('shiki/langs/javascript.mjs'); break; case 'bash': case 'sh': case 'shell': languageModule = await import('shiki/langs/bash.mjs'); break; case 'json': languageModule = await import('shiki/langs/json.mjs'); break; case 'tsx': languageModule = await import('shiki/langs/tsx.mjs'); break; case 'vue': languageModule = await import('shiki/langs/vue.mjs'); break; case 'html': languageModule = await import('shiki/langs/html.mjs'); break; default: // Fallback to typescript for unknown languages languageModule = await import('shiki/langs/typescript.mjs'); break; } const language = languageModule.default; languageCache.set(lang, language); return language; }; // Lazy load theme modules const loadTheme = async (themeName: BundledTheme): Promise<any> => { if (themeCache.has(themeName)) { return themeCache.get(themeName); } let themeModule: any; switch (themeName) { case 'github-dark': themeModule = await import('shiki/themes/github-dark.mjs'); break; case 'github-light': themeModule = await import('shiki/themes/github-light.mjs'); break; default: themeModule = await import('shiki/themes/github-light.mjs'); break; } const theme = themeModule.default; themeCache.set(themeName, theme); return theme; }; // Create a promise for highlighting const highlightCode = async ( code: string, lang: BundledLanguage, isDarkMode?: boolean ): Promise<string> => { const themeName: BundledTheme = isDarkMode ? 'github-dark' : 'github-light'; // Lazy load shiki, language, and theme in parallel const [{ codeToHtml }, languageModule, themeModule] = await Promise.all([ import('shiki/bundle/web'), loadLanguage(lang), loadTheme(themeName), ]); const shikiOptions: CodeToHastOptions<BundledLanguage, BundledTheme> = { lang, theme: themeModule, }; return codeToHtml(code, { ...shikiOptions, langs: [languageModule], } as any); }; export type CodeBlockShikiProps = { children: string; lang: BundledLanguage; isDarkMode?: boolean; }; export const CodeBlockShiki: FC<CodeBlockShikiProps> = ({ children, lang, isDarkMode, }) => { const [html, setHtml] = useState<string | null>(null); useEffect(() => { let isCancelled = false; setHtml(null); highlightCode(children, lang, isDarkMode) .then((result) => { if (!isCancelled) setHtml(result); }) .catch(() => { if (!isCancelled) setHtml(''); }); return () => { isCancelled = true; }; }, [children, lang, isDarkMode]); return ( <div className="min-w-0 max-w-full overflow-auto bg-transparent [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden [&_pre::-webkit-scrollbar]:hidden [&_pre]:[-ms-overflow-style:none] [&_pre]:[scrollbar-width:none]" style={{ backgroundColor: 'transparent', minWidth: 0, maxWidth: '100%', overflow: 'auto', }} > {html ? ( // biome-ignore lint/security/noDangerouslySetInnerHtml: Shiki generates safe HTML for code highlighting <div dangerouslySetInnerHTML={{ __html: html }} /> ) : ( <CodeDefault>{children}</CodeDefault> )} </div> ); };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/aymericzip/intlayer'

If you have feedback or need assistance with the MCP directory API, please join our Discord server