spatie/shiki-php
Use Shiki syntax highlighting from PHP. Highlight code snippets with editor-quality themes and 100+ languages, plus Antlers and Blade. Works great with Laravel via spatie/laravel-markdown and CommonMark through a companion extension.
Install the package:
composer require spatie/shiki-php
npm install shiki # or yarn add shiki
Ensure Node.js 20+ is installed.
First usage:
use Spatie\ShikiPhp\Shiki;
echo Shiki::highlight(
code: '<?php echo "Hello World"; ?>',
language: 'php',
theme: 'github-light'
);
Outputs a <pre class="shiki">...</pre> block with syntax-highlighted HTML.
Key first use cases:
Shiki::highlight(
code: file_get_contents('App/Service.php'),
language: 'php',
theme: 'dracula'
);
View Composer).spatie/laravel-markdown:
use Spatie\Markdown\MarkdownRenderer;
$renderer = new MarkdownRenderer();
$renderer->useShiki(); // Auto-highlights code blocks
echo $renderer->toHtml('# Hello **World**');
league/commonmark:
Use spatie/commonmark-shiki-highlighter for custom parsers.$userTheme = auth()->user()->preferred_theme ?? 'github-dark';
echo Shiki::highlight($code, 'php', $userTheme);
.json theme file:
Shiki::highlight($code, 'php', __DIR__.'/themes/custom-theme.json');
// Highlight specific lines (e.g., for diffs or tutorials)
Shiki::highlight(
code: $code,
language: 'php',
highlightLines: [3, '5-7'], // Lines 3, 5-7
addLines: [10], // Added line
deleteLines: [2], // Deleted line
focusLines: [4] // Focus line
);
.shiki .line--highlighted in your stylesheet.if (Shiki::languageIsAvailable('rust')) {
echo Shiki::highlight($rustCode, 'rust');
} else {
echo '<p>Unsupported language</p>';
}
$languages = Shiki::getAvailableLanguages();
$themes = Shiki::getAvailableThemes();
// app/Http/ViewComposers/CodeBlockComposer.php
public function compose(View $view) {
$view->with('highlight', function($code, $lang = 'text') {
return Shiki::highlight($code, $lang, 'github-dark');
});
}
Usage in Blade:
@highlight($codeSnippet, 'php')
return response()->json([
'code' => Shiki::highlight($code, 'javascript', 'vscode-dark-plus'),
]);
$cacheKey = "shiki_{$lang}_{md5($code)}";
$highlighted = Cache::remember($cacheKey, now()->addHours(1), function() use ($code, $lang) {
return Shiki::highlight($code, $lang);
});
// Register a custom directive
Blade::directive('highlight', function($expression) {
return "<?php echo \\Spatie\\ShikiPhp\\Shiki::highlight($expression, 'php'); ?>";
});
Usage:
@highlight('$user->name')
Large Code Blocks:
stdin for files >4KB (avoids proc_open() errors):
Shiki::highlight(file_get_contents('large-file.php'), 'php');
Batch Processing:
$highlights = collect($codeBlocks)->map(function($block) {
return Shiki::highlight($block['code'], $block['lang']);
});
Queue Jobs:
// For async rendering (e.g., user-uploaded files)
HighlightCodeJob::dispatch($filePath, $language);
Node.js Path Issues:
sudo ln -s ~/.nvm/versions/node/v20.x.x/bin/node /usr/local/bin/node
Shiki::getNodePath(); // Returns the detected path
Large Code Blocks:
proc_open(): posix_spawn() failed: Argument list too longshiki-php v2.3.3+ (uses stdin for large inputs).Theme/Language Availability:
Shiki::getAvailableThemes() to verify themes.Blade/Antlers Quirks:
@ symbols are escaped in code (e.g., @@yield).Caching Caveats:
md5($code.$lang)).Log Output:
$html = Shiki::highlight($code, 'php');
\Log::debug($html); // Inspect raw HTML
Validate Input:
if (!Shiki::languageIsAvailable($lang)) {
throw new \InvalidArgumentException("Unsupported language: {$lang}");
}
Test Themes:
// Test all themes in a loop
foreach (Shiki::getAvailableThemes() as $theme) {
echo Shiki::highlight('test', 'text', $theme);
}
Node Version Conflicts:
Error: Cannot find module 'shiki'node_modules/shiki exists in your project root.Custom Renderers:
Shiki::setRendererScript(__DIR__.'/custom-renderer.js');
Post-Processing:
$html = Shiki::highlight($code, 'php');
$html = str_replace('<span class="line">', '<span class="line" data-line="', $html);
Event Listeners:
highlight events (e.g., log usage):
Shiki::highlight($code, 'php')->then(function($html) {
\Log::info('Highlighted code', ['length' => strlen($html)]);
});
Custom Languages:
node_modules/shiki/langs.Dark/Light Mode Toggle:
$theme = request()->wantsJson() ? 'github-dark' : 'github-light';
Line Numbers:
.shiki .line {
counter-increment: line-number;
}
.shiki .line::before {
content: counter(line-number);
margin-right: 1rem;
}
Copy-to-Clipboard:
<button onclick="copyToClipboard('{{ $highlightedHtml }}')">Copy</button>
<script>
function copyToClipboard(html) {
const temp = document.createElement('textarea');
temp.value = html.replace(/<[^>]*>/g
How can I help you explore Laravel packages today?