Fine-grained Markdown
Flexible content, richer presentation
Markdown is great. Easy to write, easy to read, and covers most use cases for content websites.
But sometimes it feels a bit limited. Sometimes your content is not so linear, it has more structure, and you want to present it in a way that reflects that structure. You want a richer medium to present your content.
Let me show you what I mean, and how Code Hike can help you solve this problem.
Rendering markdown using React usually looks something like this:
# HelloLorem ipsum dolor sit## Step oneAmet consectetur adipiscing## Step twoElit sed do eiusmod## Step threeTempor incididunt ut labore
import Content from "./content.md"export function Page() {return (<div className="..."><SideBar /><Content /></div>)}
We get the content as a component. We can render the rest of the page any way we want, but the content itself is a single rigid block.
Frontmatter
We can convert part of the content into metadata using frontmatter.
---title: Hello---Lorem ipsum dolor sit## Step oneAmet consectetur adipiscing## Step twoElit sed do eiusmod## Step three
import {Content,frontmatter,} from "./content.md"const { title } = frontmatterexport function Page() {return (<div><SideBar /><main><h1>{title}</h1><Content /></main></div>)}
This is a good start, it gives us some flexibility, but the main content is still a single block of markdown.
If we want more flexibility, we need a way to break the content into smaller pieces.
Code Hike Blocks
To make the content flexible, Code Hike introduces the concept of blocks.
## !intro HelloLorem ipsum dolor sit## !!steps Step oneAmet consectetur adipiscing## !!steps Step twoElit sed do eiusmod## !!steps Step threeTempor incididunt ut labore
import Content from "./content.md"import { parse } from "codehike"const { intro, steps } = parse(Content)export function Page() {return (<div><SideBar /><main>{intro.children}{steps.map((step) => (<div><h2>{step.title}</h2>{step.children}</div>))}</main></div>)}
We define blocks by adding a special decoration to markdown headings.
Blocks can now be destructured from the content, giving us fine-grained control to render them any way we want.
We can go even further and decorate paragraphs, images, and codeblocks.
For example, we can add a cover image to each step.
## !intro HelloLorem ipsum dolor sit## !!steps Step one![!cover](/one.png)Amet consectetur adipiscing## !!steps Step two![!cover](/two.png)## !!steps Step three
import Content from "./content.md"import { parse } from "codehike"const { intro, steps } = parse(Content)export function Page() {return (<div><SideBar /><main>{intro.children}{steps.map((step) => (<div><img src={step.cover} /><h2>{step.title}</h2>{step.children}</div>))}</main></div>)}
Content Schema
We can define a schema so we get autocompletion and all the benefits of typescript in the editor.
## !intro HelloLorem ipsum dolor sit## !!steps Step one![!cover](/one.png)Amet consectetur adipiscing## !!steps Step two![!cover](/two.png)## !!steps Step three
import Content from "./content.md"import {parse,Block,ImageBlock,} from "codehike"import { z } from "zod"const Schema = Block.extend({intro: Block,steps: z.array(Block.extend({ cover: ImageBlock }),),})const { intro, steps } = parse(Content,Schema,)export function Page() {return <div>...</div>}
Type-safe Markdown
Adding a schema also means that now we validate that the content follows the structure we defined.
For example, since we defined that each step should have a cover image, we get a type error if we forget to add the image inside a step.
## !intro HelloLorem ipsum dolor sit## !!steps Step one> no cover image!Amet consectetur adipiscing## !!steps Step two![!cover](/two.png)## !!steps Step three
import Content from "./content.md"import {parse,Block,ImageBlock,} from "codehike"import { z } from "zod"const Schema = Block.extend({intro: Block,steps: z.array(Block.extend({ cover: ImageBlock }),),})const { intro, steps } = parse(Content,Schema,)export function Page() {return <div>...</div>}
Unhandled Error
Error at:
## !!steps Step one
missing `cover`
import Content from "./content.md"export function Page() {return (<div className="..."><SideBar /><Content /></div>)}
# HelloLorem ipsum dolor sit## Step oneAmet consectetur adipiscing## Step twoElit sed do eiusmod## Step threeTempor incididunt ut labore
We get the content as a component. We can render the rest of the page any way we want, but the content itself is a single rigid block.
Frontmatter
We can convert part of the content into metadata using frontmatter.
This is a good start, it gives us some flexibility, but the main content is still a single block of markdown.
If we want more flexibility, we need a way to break the content into smaller pieces.
Code Hike Blocks
To make the content flexible, Code Hike introduces the concept of blocks.
We define blocks by adding a special decoration to markdown headings.
Blocks can now be destructured from the content, giving us fine-grained control to render them any way we want.
We can go even further and decorate paragraphs, images, and codeblocks.
For example, we can add a cover image to each step.
Content Schema
We can define a schema so we get autocompletion and all the benefits of typescript in the editor.
Type-safe Markdown
Adding a schema also means that now we validate that the content follows the structure we defined.
For example, since we defined that each step should have a cover image, we get a type error if we forget to add the image inside a step.
And that's how Code Hike gives you type-safe fine-grained markdown, making your content more flexible so you can use the whole power of React to present it in any way you want.
Learn more: