From Markdown to Video
Animated code walkthroughs with Code Hike and Remotion
For a long time, many people have asked for a way to combine Code Hike and Remotion to turn code walkthroughs into videos. Because of Code Hike limitations, this wasn't possible without a lot of hacks. Until now.
This changes with Code Hike v1.
With the new version of Code Hike you can now use custom react components for everything. Also, v1 allows you to pass structured markdown content to these components in any shape you want.
The process from markdown to video is something like this:
- Use Code Hike to pass annotated markdown content (and codeblocks) to your components.
- In your components, use Code Hike and Remotion to animate annotations and transitions.
- Use Remotion to render your components into videos.
Let's see how this works.
In this example I'm not including the project configuration. If you want to follow along, you can find the full code in the Code Hike examples repository.
Rendering steps
from Markdown
We start by importing a markdown file:
## !!steps OneLorem## !!steps TwoIpsum
Then we can use Code Hike's parseRoot
to trasform the markdown into a list:
const steps = [{title: "One",children: <p>Lorem</p>,},{title: "Two",children: <p>Ipsum</p>,},]
We can pass those steps
to a Video
component and render a Sequence
for each step:
<AbsoluteFill><Sequencetitle="One"from={0}durationInFrames={60}><p>Lorem</p></Sequence><Sequencetitle="Two"from={60}durationInFrames={60}><p>Ipsum</p></Sequence></AbsoluteFill>
This tells Remotion to render Lorem
for the first 60 frames and then Ipsum
from frame 60 to frame 120.
The output:
import Content from "./content.md"import {parseRoot,Block,} from "code-hike/blocks"import { z } from "zod"const Schema = Block.extend({steps: z.array(Block),})const { steps } = parseRoot(Content,Schema,)import {AbsoluteFill,Sequence,Composition,} from "remotion"const STEP_FRAMES = 60function Video({ steps }) {return (<AbsoluteFillstyle={{ background: "#0D1117" }}>{steps.map((step, i) => (<Sequencelayout="none"name={step.title}from={STEP_FRAMES * i}durationInFrames={STEP_FRAMES}>{step.children}</Sequence>))}</AbsoluteFill>)}export function RemotionRoot() {return (<Compositionid="CodeHikeExample"component={Video}defaultProps={{ steps }}width={300}height={200}fps={60}durationInFrames={STEP_FRAMES * steps.length}/>)}
So far nothing too fancy.
Rendering Codeblocks
Things get interesting when we start adding annotated codeblocks.
## !!steps One```js !let lorem = 1lorem += 2```## !!steps Two```js !let lorem = 1if (ipsum) {lorem += 2}```
We can change the content and the Schema
to include codeblocks.
Now each step from steps
will have a code
property.
Rendering code
We can use Code Hike's Pre
component to render the code.
Now the output video shows each codeblock for one second:
Adding annotations
We can add annotations to the codeblocks:
## !!steps One```js !let lorem = 1lorem += 2```## !!steps Two```js !// !marklet lorem = 1if (ipsum) {// !marklorem += 2}```
And then define an annotation handler to tell Code Hike what component should use to render those annotations.
For more examples and inspiration of what you can do with annotations, check the code examples.
Animating annotations
Since annotations are handled by React components, we can use Remotion hooks inside them.
For example, we can use the current frame together with interpolateColors
to add a fade-in effect.
This is the output:
The frame is relative to the current <Sequence />
, so the fade-in effect happens from the 10th frame to the 20th frame of the second sequence.
We can also make the delay and duration of the annotations parametrizable, by using the annotation query.
## !!steps One```js !let lorem = 1lorem += 2```## !!steps Two```js !// !mark 10let lorem = 1if (ipsum) {// !mark 25lorem += 2}```
annotation.query
is the string after the annotation name. In this case, "10"
and "25"
.
Animating the code
Finally, we can use Code Hike's token transition utils to make the transition between the codeblocks more interesting:
import Content from "./content.md"const Schema = Block.extend({steps: z.array(Block.extend({code: HighlightedCodeBlock,}),),})const { steps } = parseRoot(Content,Schema,)const STEP_FRAMES = 60function Video({ steps }) {return (<AbsoluteFillstyle={{background: "#0D1117",alignItems: "center",}}>{steps.map((step, i) => (<Sequencelayout="none"name={step.title}from={STEP_FRAMES * i}durationInFrames={STEP_FRAMES}><Code code={step.code} /></Sequence>))}</AbsoluteFill>)}export function RemotionRoot() {return (<Compositionid="CodeHikeExample"component={Video}defaultProps={{ steps }}durationInFrames={STEP_FRAMES * steps.length}fps={60}width={140}height={90}/>)}
Examples
You can find the full code in the Code Hike examples repository.
The repository also includes some extra examples: