Elevate Your Astro Code Highlights with TypeScript Snippets
Want to take your Astro code highlights to the next level? This guide will show you how to implement TypeScript snippets with hover-over type information, making your code examples more interactive and informative.
Prerequisites for Astro Code Highlights
Before diving into advanced Astro code highlights, ensure you have an Astro project set up. If you need assistance, refer to the official Astro quickstart guide.
Configuring Shiki for Enhanced Astro Code Highlights
Astro comes with Shiki built-in, providing excellent syntax highlighting. Let’s optimize it for our TypeScript snippets:
- Modify your
astro.config.mjs
:
import { function defineConfig(config: AstroUserConfig): AstroUserConfig
See the full Astro Configuration API Documentation
https://astro.build/configdefineConfig } from "astro/config";
export default function defineConfig(config: AstroUserConfig): AstroUserConfig
See the full Astro Configuration API Documentation
https://astro.build/configdefineConfig({
AstroUserConfig.markdown?: {
shikiConfig?: Partial<ShikiConfig>;
syntaxHighlight?: "shiki" | "prism" | false;
remarkPlugins?: RemarkPlugins;
rehypePlugins?: RehypePlugins;
gfm?: boolean;
smartypants?: boolean;
remarkRehype?: RemarkRehype;
} | undefined
markdown: {
shikiConfig?: Partial<ShikiConfig> | undefined
shikiConfig: {
themes?: Record<string, ThemePresets | ThemeRegistration | ThemeRegistrationRaw> | undefined
themes: { light: "min-light"
light: "min-light", dark: "tokyo-night"
dark: "tokyo-night" },
},
},
});
- Add a stylish border to your code blocks:
pre:has(code) {
@apply border border-skin-line;
}
Integrating Twoslash for Interactive Type Information
To make your Astro code highlights truly interactive, we’ll add Twoslash:
- Install the necessary packages:
npm i -D twoslash twoslash-vue
- Update your
astro.config.mjs
to include Twoslash:
import { function createTransformerFactory(defaultTwoslasher: TwoslashShikiFunction | TwoslashGenericFunction, defaultRenderer?: TwoslashRenderer): (options?: TransformerTwoslashOptions) => ShikiTransformer
createTransformerFactory, function rendererRich(options?: RendererRichOptions): TwoslashRenderer
An alternative renderer that providers better prefixed class names,
with syntax highlight for the info text.rendererRich } from '@shikijs/twoslash/core'
import { function createTwoslasher(createOptions?: CreateTwoslashVueOptions): TwoslashInstance
Create a twoslasher instance that add additional support for Vue SFC.createTwoslasher } from 'twoslash-vue'
import { function defineConfig(config: AstroUserConfig): AstroUserConfig
See the full Astro Configuration API Documentation
https://astro.build/configdefineConfig } from "astro/config";
export default function defineConfig(config: AstroUserConfig): AstroUserConfig
See the full Astro Configuration API Documentation
https://astro.build/configdefineConfig({
AstroUserConfig.markdown?: {
shikiConfig?: Partial<ShikiConfig>;
syntaxHighlight?: "shiki" | "prism" | false;
remarkPlugins?: RemarkPlugins;
rehypePlugins?: RehypePlugins;
gfm?: boolean;
smartypants?: boolean;
remarkRehype?: RemarkRehype;
} | undefined
markdown: {
shikiConfig?: Partial<ShikiConfig> | undefined
shikiConfig: {
themes?: Record<string, ThemePresets | ThemeRegistration | ThemeRegistrationRaw> | undefined
themes: { light: "min-light"
light: "min-light", dark: "tokyo-night"
dark: "tokyo-night" },
transformers?: ShikiTransformer[] | undefined
transformers: [
function createTransformerFactory(defaultTwoslasher: TwoslashShikiFunction | TwoslashGenericFunction, defaultRenderer?: TwoslashRenderer): (options?: TransformerTwoslashOptions) => ShikiTransformer
createTransformerFactory(function createTwoslasher(createOptions?: CreateTwoslashVueOptions): TwoslashInstance
Create a twoslasher instance that add additional support for Vue SFC.createTwoslasher())({
TransformerTwoslashOptions.langs?: string[] | undefined
Languages to apply this transformer tolangs: ['ts', 'tsx', 'vue'],
TransformerTwoslashOptions.renderer?: TwoslashRenderer | undefined
Custom renderers to decide how each info should be renderedrenderer: function rendererRich(options?: RendererRichOptions): TwoslashRenderer
An alternative renderer that providers better prefixed class names,
with syntax highlight for the info text.rendererRich({
RendererRichOptions.lang?: string | undefined
Language for syntax highlight.lang: 'ts',
}),
}),
],
},
},
});
Showcasing Interactive TypeScript Snippets in Astro
Now you can create dynamic TypeScript snippets in your Markdown files:
interface User {
User.name: string
name: string;
User.age: number
age: number;
}
const const user: User
user: User = {
User.name: string
name: "John Doe",
User.age: number
age: 30
};
var console: Console
The `console` module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
* A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream.
* A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and
[`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without calling `require('console')`.
_**Warning**_: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for
more information.
Example using the global `console`:
```js
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
```
Example using the `Console` class:
```js
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
```console.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html)
(the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)).
```js
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
```
See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.log(const user: User
user.User.name: string
name);
The // ^?
marker indicates where type information will appear on hover, enhancing your Astro code highlights.
Vue snippets are also supported:
<script setup lang="ts">
import { function ref<T>(value: T): [T] extends [Ref] ? IfAny<T, Ref<T>, T> : Ref<UnwrapRef<T>, UnwrapRef<T> | T> (+1 overload)
Takes an inner value and returns a reactive and mutable ref object, which
has a single property `.value` that points to the inner value.ref } from 'vue'
const { const title: string
title } = const defineProps: <{
title: string;
}>() => DefineProps<LooseRequired<{
title: string;
}>, never> (+2 overloads)
Vue `<script setup>` compiler macro for declaring component props. The
expected argument is the same as the component `props` option.
Example runtime declaration:
```js
// using Array syntax
const props = defineProps(['foo', 'bar'])
// using Object syntax
const props = defineProps({
foo: String,
bar: {
type: Number,
required: true
}
})
```
Equivalent type-based declaration:
```ts
// will be compiled into equivalent runtime declarations
const props = defineProps<{
foo?: string
bar: number
}>()
```defineProps<{
title: string
title: string
}>()
const const count: Ref<number, number>
count = ref<number>(value: number): Ref<number, number> (+1 overload)
Takes an inner value and returns a reactive and mutable ref object, which
has a single property `.value` that points to the inner value.ref(0)
const const increment: () => void
increment = () => {
const count: Ref<number, number>
count.- value
v
}
const const decrement: () => void
decrement = () => {
const count: Ref<number, number>
count.Ref<number, number>.value: number
value--
}
</script>
<template>
<div: HTMLAttributes & ReservedProps
div>
<h2: HTMLAttributes & ReservedProps
h2>{{ const title: string
title }}</h2: HTMLAttributes & ReservedProps
h2>
<p: HTMLAttributes & ReservedProps
p>Count: {{ const count: Ref<number, number>
count }}</p: HTMLAttributes & ReservedProps
p>
<button: ButtonHTMLAttributes & ReservedProps
button @onClick?: ((payload: MouseEvent) => void) | undefined
click="const increment: () => void
increment">Increment</button: ButtonHTMLAttributes & ReservedProps
button>
<button: ButtonHTMLAttributes & ReservedProps
button @onClick?: ((payload: MouseEvent) => void) | undefined
click="const decrement: () => void
decrement">Decrement</button: ButtonHTMLAttributes & ReservedProps
button>
</div: HTMLAttributes & ReservedProps
div>
</template>
For autocompletion suggestions, use the ^|
marker:
const increment = () => {
count.v
// ^|
}
Benefits of Enhanced Astro Code Highlights
By implementing these features, your Astro site now boasts:
- Advanced syntax highlighting
- Interactive hover-over type information
- Adaptive light and dark mode code blocks
These improvements significantly enhance code readability and user experience, making your Astro code highlights more valuable to your readers.