
[August 2026 edition]
How to Embed VHS in Astro
Published: Aug 17, 2026
Reading time: ~5 min
When explaining terminal operations in a blog, pasting commands into a code block does not always make it clear where input happens or what result appears.
That is why we introduced VHS, which shows both command input and output. We use VHS-generated demos in Astro Markdown articles. This is a case study of that work.
What is VHS?
VHS is an open-source terminal tool that runs commands written in a .tape file on a virtual terminal and generates GIF, MP4, WebM, or PNG output that reproduces the input and result. Typing speed, wait times, and screen size can also be specified in the tape.
Unlike a screen recording, a tape lets us reproduce exactly the same steps repeatedly by keeping typing speed and wait times consistent. The same tape can produce an MP4 for the blog, a GIF for X, and a PNG showing the result.
The demo on this page was generated in the same way.
>
────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────
> echo 'Astro + VHS'
────────────────────────────────────────────────────────────────────────────────
> echo 'Astro + VHS'
Astro + VHS
>
────────────────────────────────────────────────────────────────────────────────
> echo 'Astro + VHS'
Astro + VHS
>
────────────────────────────────────────────────────────────────────────────────
> echo 'Astro + VHS'
Astro + VHS
> echo 'demo generated by VHS'
────────────────────────────────────────────────────────────────────────────────
> echo 'Astro + VHS'
Astro + VHS
> echo 'demo generated by VHS'
demo generated by VHS
>
────────────────────────────────────────────────────────────────────────────────
> echo 'Astro + VHS'
Astro + VHS
> echo 'demo generated by VHS'
demo generated by VHS
>
────────────────────────────────────────────────────────────────────────────────
> echo 'Astro + VHS'
Astro + VHS
> echo 'demo generated by VHS'
demo generated by VHS
>
────────────────────────────────────────────────────────────────────────────────
> echo 'Astro + VHS'
Astro + VHS
> echo 'demo generated by VHS'
demo generated by VHS
>
────────────────────────────────────────────────────────────────────────────────
VHS can also write the final terminal contents to a text file by specifying .txt or .ascii in Output. On this site, a button in the top-right of the video area copies that result.
How to embed VHS in Astro
On this site, we use video as the code-block language and let the block contain values such as the media URL and title. This lets us display a terminal demo in the article.
In an Astro Markdown file, we write the following:
```video
src: /videos/astro-vhs-blog/demo.mp4
```We register the existing Remark plugins together with the terminal-video plugin in the Astro configuration.
// astro.config.mjs
import { remarkTerminalVideo } from './src/lib/remark-terminal-video';
export default defineConfig({
markdown: {
remarkPlugins: [
remarkCodeTitles,
remarkReadingTime,
remarkTerminalVideo,
],
},
});We made the shared video display available across the site without importing an Astro component in every article. Astro Markdown processing and Markdown plugins are also covered in the Astro documentation.
Example of a VHS tape used on this site
On this site, we created a terminal demo from a tape using echo to show the flow of input, processing, and results.
Output public/videos/analyze-project/demo.mp4
Output public/videos/analyze-project/demo.gif
Output public/videos/analyze-project/demo.txt
Require echo
Set Shell "bash"
Set Width 960
Set Height 540
Set FontSize 22
Set Padding 8
Set CursorBlink false
Set Framerate 10
Set TypingSpeed 65ms
Hide
Type "clear"
Enter
Type "echo 'analyzing sample-project...'"
Enter
Sleep 1s
Type "echo 'files: 42 dependencies: 8 status: ready'"
Enter
Sleep 2s
Screenshot public/videos/analyze-project/demo.png
ShowIn this tape, Set Width and Set Height specify the terminal size, while Type, Enter, and Sleep represent input and processing time. MP4, GIF, and PNG are generated at this size and displayed in the article without changing their aspect ratio. On smartphones, they shrink to fit the screen width.
On this site, we keep the input data fixed so that the same demo is produced every time. Commands that cannot be run repeatedly in production are reproduced with a --demo mode.
Output generates the video, GIF, and text files, while Screenshot generates the poster image.
Terminal video display and its attributes
On this site, a code block whose Markdown language is video is displayed as a terminal demo. We read the block’s key: value settings and display a video player, GIF, or PNG according to format. We add title and caption, and when text is specified, we show a button for copying the command result. Other code blocks and Markdown are left unchanged.
Media URLs are limited to site-relative paths or HTTPS. We reject javascript:, data:, control characters, and quotes that could break an HTML attribute, and escape article values when displaying them. This prevents article settings from being interpreted as unintended HTML.
The attributes can be specified together as follows.
```video
format: video
src: /videos/astro-vhs-blog/demo.mp4
webm: /videos/astro-vhs-blog/demo.webm
poster: /videos/astro-vhs-blog/demo.png
text: /videos/astro-vhs-blog/demo.txt
title: Project analysis terminal demo
caption: Run a command and display the analysis result
autoplay: true
controls: true
loop: true
muted: true
playsinline: true
preload: metadata
fallback: /videos/astro-vhs-blog/demo.mp4
```src: Specifies the video or image to display. Required.webm: Specifies a WebM video.poster: Specifies the image shown before a video starts.text: Specifies the text file used for copying the command result.title: Specifies the demo title.caption: Specifies a description for the video or image.autoplay: Starts playback when the page opens.controls: Shows playback, pause, volume, and other controls.loop: Repeats playback.muted: Plays without sound.playsinline: Keeps playback in the page on smartphones.preload: Specifies how the video is loaded. Withmetadata, the whole video is not loaded when the page opens.fallback: Specifies alternative content when the video cannot be played.
We enable controls, loop, muted, and playsinline by default. autoplay is enabled only when specified.
format switches the display format.
| format | Display | Suitable for |
|---|---|---|
video | Video player | Interactive controls, posters, MP4/WebM, longer demos |
gif | Repeating image | Short, silent loops for X and quick checks |
png | Still image | Saved results and video posters |
For GIF and PNG, alt describes the image content. For example:
```video
format: gif
src: /videos/astro-vhs-blog/demo.gif
text: /videos/astro-vhs-blog/demo.txt
alt: Terminal animation generated by VHS
caption: The GIF loops automatically as an image
```Here is the rendered example.

>
────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────
> echo 'Astro + VHS'
────────────────────────────────────────────────────────────────────────────────
> echo 'Astro + VHS'
Astro + VHS
>
────────────────────────────────────────────────────────────────────────────────
> echo 'Astro + VHS'
Astro + VHS
>
────────────────────────────────────────────────────────────────────────────────
> echo 'Astro + VHS'
Astro + VHS
> echo 'demo generated by VHS'
────────────────────────────────────────────────────────────────────────────────
> echo 'Astro + VHS'
Astro + VHS
> echo 'demo generated by VHS'
demo generated by VHS
>
────────────────────────────────────────────────────────────────────────────────
> echo 'Astro + VHS'
Astro + VHS
> echo 'demo generated by VHS'
demo generated by VHS
>
────────────────────────────────────────────────────────────────────────────────
> echo 'Astro + VHS'
Astro + VHS
> echo 'demo generated by VHS'
demo generated by VHS
>
────────────────────────────────────────────────────────────────────────────────
> echo 'Astro + VHS'
Astro + VHS
> echo 'demo generated by VHS'
demo generated by VHS
>
────────────────────────────────────────────────────────────────────────────────
```video
format: png
src: /videos/astro-vhs-blog/demo.png
text: /videos/astro-vhs-blog/demo.txt
alt: Still image of a terminal generated by VHS
caption: The PNG is displayed as a poster or still image
```Here is the rendered PNG example.

>
────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────
>
────────────────────────────────────────────────────────────────────────────────
> echo 'Astro + VHS'
────────────────────────────────────────────────────────────────────────────────
> echo 'Astro + VHS'
Astro + VHS
>
────────────────────────────────────────────────────────────────────────────────
> echo 'Astro + VHS'
Astro + VHS
>
────────────────────────────────────────────────────────────────────────────────
> echo 'Astro + VHS'
Astro + VHS
> echo 'demo generated by VHS'
────────────────────────────────────────────────────────────────────────────────
> echo 'Astro + VHS'
Astro + VHS
> echo 'demo generated by VHS'
demo generated by VHS
>
────────────────────────────────────────────────────────────────────────────────
> echo 'Astro + VHS'
Astro + VHS
> echo 'demo generated by VHS'
demo generated by VHS
>
────────────────────────────────────────────────────────────────────────────────
> echo 'Astro + VHS'
Astro + VHS
> echo 'demo generated by VHS'
demo generated by VHS
>
────────────────────────────────────────────────────────────────────────────────
> echo 'Astro + VHS'
Astro + VHS
> echo 'demo generated by VHS'
demo generated by VHS
>
────────────────────────────────────────────────────────────────────────────────
We adjust the CSS so that videos and images match the article width and fit within the viewport on smartphones.
Example of how this site manages VHS outputs
The following is an example of how this site groups a tape with the files generated from it.
tapes/
└── <demo-name>.tape
public/videos/<article-slug>/
├── demo.mp4
├── demo.gif
└── demo.pngUpdating only the video file makes it hard to tell which steps and settings produced it. That is why we keep the tape together with the files generated from the same demo.
Summary
We generate MP4, GIF, and PNG files from VHS tapes and display them in Astro articles with a video code block. We also added format switching and a button in the video area for copying the command result.
Thank you for your continued support of the Asopi Tech site.