Blocks

You can decorate markdown elements with a special syntax, Code Hike will transform them into objects and pass them as props to your components.

This lets you add structure to your markdown content that you can then use to render it in any way you want using React components.

content.mdx
import { MyComponent } from "./my-component"
<MyComponent>
The two towers
## !mordor Barad-dûr
The Dark Tower
Sauron's fortress
## !isengard Orthanc
Saruman's stronghold
</MyComponent>
my-component.jsx
export function MyComponent(props) {
props = {
children: <p>The two towers</p>,
mordor: {
title: "Barad-dûr",
children: (
<>
<p>The Dark Tower</p>
<p>Sauron's fortress</p>
</>
),
},
isengard: {
title: "Orthanc",
children: <p>Saruman's stronghold</p>,
},
}
...
}
Code Hike transforming decorated markdown into props

The !mordor decoration at the start of the first heading tells Code Hike to group the content between this heading and the next one, the !isengard heading. That content group becomes a block object, that you can then use in your components.

Images, CodeBlocks, and Paragraphs

Besides headings, you can add the ! decoration to images, codeblocks, and paragraphs.

content.mdx
import { MyComponent } from "./my-component"
<MyComponent>
The Fellowship of the Ring
!author Tolkien
![!cover Gandalf](/gandalf.jpg "a wizard")
```js !riddle mellon.js
speak("friend")
```
## !moria western gate
Speak, friend, and enter
</MyComponent>
my-component.jsx
export function MyComponent(props) {
props = {
children: <p>The Fellowship of the Ring</p>,
author: "Tolkien",
cover: {
alt: "Gandalf",
url: "/gandalf.jpg",
title: "a wizard",
},
riddle: {
lang: "js",
meta: "mellon.js",
value: 'speak("friend")',
},
moria: {
title: "western gate",
children: <p>Speak, friend, and enter</p>,
},
}
...
}
Decorated paragraphs, images, and codeblocks

Lists

You can use !!, instead of !, to list all the blocks with the same decoration in an array.

content.mdx
import { MyComponent } from "./my-component"
<MyComponent>
The Brandybuck Brunch
## !!breakfasts first
Grilled mushrooms
## !!breakfasts second
Apple pancakes
</MyComponent>
my-component.jsx
export function MyComponent(props) {
props = {
children: <p>The Brandybuck Brunch</p>,
breakfasts: [
{
title: "first",
children: <p>Grilled mushrooms</p>,
},
{
title: "second",
children: <p>Apple pancakes</p>,
},
],
}
...
}
Using !! for lists

The same applies to images, codeblocks, and paragraphs.

Nesting

You can use headigns with different levels to create nested blocks.

content.mdx
import { MyComponent } from "./my-component"
<MyComponent>
The Rings of Power
## !master
The One Ring
### !!rings Elves
Three rings
### !!rings Dwarves
Seven rings
### !!rings Men
Nine rings
</MyComponent>
my-component.jsx
export function MyComponent(props) {
props = {
children: <p>The Rings of Power</p>,
master: {
title: "",
children: <p>The One Ring</p>,
rings: [
{
title: "Elves",
children: <p>Three rings</p>,
},
{
title: "Dwarves",
children: <p>Seven rings</p>,
},
{
title: "Men",
children: <p>Nine rings</p>,
},
],
},
}
...
}
Using header levels for nesting

Schema

You can use zod schemas to validate the content coming from the MDX. This has two main benefits:

  • Type-safe markdown: You'll see an error if the content doesn't match the schema
  • Better tooling:You'll get autocompletion and type checking in your editor
content.mdx
import { MyComponent } from "./my-component"
<MyComponent>
The Fellowship of the Ring
!author Tolkien
![!cover Gandalf](/gandalf.jpg "a wizard")
```js !riddle mellon.js
speak("friend")
```
## !!breakfasts first
Grilled mushrooms
## !!breakfasts second
Apple pancakes
</MyComponent>
my-component.tsx
import { z } from "zod"
import {
parseProps, Block, CodeBlock, ImageBlock
} from "codehike/blocks"
const Schema = Block.extend({
author: z.string(),
cover: ImageBlock.optional(),
riddle: CodeBlock,
breakfasts: z.array(Block),
})
export function MyComponent(props) {
const data = parseProps(props, Schema)
const data: {
title: string
children?: ReactNode
author: string
cover?: {
alt: string
url: string
title: string
}
riddle: {
lang: string
meta: string
value: string
}
breakfasts: {
title: string
children?: ReactNode
}[]
}
...
}
Using schemas for markdown validation and typing

Root level blocks

You can use decorated elements directly in the root of your Markdown/MDX file.

content.md
The Brandybuck Brunch
## !!breakfasts first
Grilled mushrooms
## !!breakfasts second
Apple pancakes
page.jsx
import { parseRoot } from "codehike"
import MDX from "./content.md"
const Schema = Block.extend({
breakfasts: z.array(Block),
})
export default function Page() {
const data = parseRoot(MDX, Schema)
...
}
Using decorated elements in plain markdown

Component blocks

Coming soon

Examples

The code tooltip example shows how to use blocks at a component level.

The scrollycoding example shows how to use blocks for layout at a page level.