A tour of the new MDX-powered writing surface β split layouts, pull quotes, galleries, embeds, and the retention mechanics behind the scenes.

Yogesh Mishra

I rebuilt this blog from scratch. The old one was a single HTML string dumped into a div β fine for a paragraph, hopeless for anything that actually wants to breathe. The new surface is MDX: every post is a file, every layout primitive is a JSX component, and the rules of the page are the rules of the piece.
This post is the showcase. Every component below is one you can reach for when writing.
The <Split> component is the workhorse. Image on one side, prose on the other. Swap the side, tune the ratio, stack cleanly on mobile.

This is what a standard editorial split looks like. The image sits flush with the column and the text gets to be text β not a caption, not a sidebar, just writing at a comfortable measure.
Use this when the image is supporting the argument, not carrying it.

When the visual is the point, flip the ratio. The 2:3 here gives the picture more room and the text becomes a deliberate caption-length counterweight.
Works well for product shots, hero moments, diagrams that want to be read first.
Sometimes you want a picture to stop the scroll. <Figure> with aspect="full-bleed" breaks out of the prose column without breaking the layout.
Found this useful?
Tap a reaction β it helps me know what to write next.

The contained variant keeps things polite:

βThe most impressive people I know are all terrible procrastinators. So could it be that procrastination isn't always bad?
Pull quotes are for the sentence you want the reader to feel, not just read. Use them sparingly β one per post is usually enough. Two is a maximum. Three means you should probably cut the piece.
Callouts are how you step outside the argument to say something about it. Four variants, each with its own tone:
Three images, one row. <Gallery cols={3}> and let CSS grid do the rest.



The <Stats> component is for the moment you need to land a figure. Count-up triggers on scroll, respects prefers-reduced-motion.
Some arguments need a shape. <Chart> is an inline-SVG primitive β no external dep, no client bloat, animates in on view, and respects prefers-reduced-motion. Three types: bar, line, area.
Audited on a throttled 4G connection, mid-tier mobile.
Each release shipped one specific optimization β measured, never guessed.
Hover (or focus) any data point for the exact value. The chart doesn't try to be everything; it's there when you have five-to-ten numbers and want them to breathe.
Two ways to add diagrams. If you have an image file (screenshot, exported Figma, ER tool export), use <Diagram> β it wraps the image in a labeled, click-to-zoom container with lightbox:

If you want to write the diagram as code, use <Mermaid> β it renders flowcharts, ER diagrams, sequence diagrams, class diagrams, and more from text syntax:
Use label to describe the diagram type. Good labels: ER Diagram, Flow Chart, Schema, Sequence, Architecture.
The <Steps> component is for ordered processes β deploy pipelines, remediation plans, onboarding flows. Not a file tree, not a list β a proper timeline with status indicators.
Writing a blog post
Put it in a Quote on a scratch line. If it doesn't survive being pulled out, rewrite it.
A Stats row, a Figure, a Gallery, a Mermaid diagram β whatever makes the argument concrete.
Two Split blocks, a Divider, then tighter paragraphs. Structure before prose.
What should the reader do at the end? A single inline CTA beats three scattered ones.
The <FileTree> component renders an interactive folder tree β collapsible directories, file-type icons, and optional badges to highlight changes.
Badges tell the reader what changed: new (green), modified (amber), deleted (red + strikethrough). Annotations after the filename add context without cluttering the tree. Directories auto-sort alphabetically, folders before files.
Fenced blocks use Shiki via rehype-pretty-code β server-rendered, zero client cost, with line highlighting syntax ({1,3-5}).
export function estimateReadTime(markdown: string): number {
const text = markdown
.replace(/```[\s\S]*?```/g, '') // strip code fences
.replace(/<[^>]+>/g, '') // strip HTML
.replace(/[#>*_`~\[\]()!-]/g, ' '); // strip md punctuation
const words = text.split(/\s+/).filter(Boolean).length;
return Math.max(1, Math.round(words / 220));
}Inline code like useDeferredValue gets the pill treatment so it doesn't get lost in running text.
YouTube embeds use the lite-youtube pattern β a poster image until you click, then the real iframe. Zero cost on load, full experience on intent.
Dots for a soft break. A plain <Divider /> gives you a hairline. Use them between movements when a new heading would feel like a new chapter.
Everything above is the surface. Under it, the blog runs a handful of small mechanics designed to respect your time and reward your attention:
localStorage, and a tiny burst of particles.If you're about to write a post in this system, start with the structure, not the prose:
<Quote> on a scratch line. If it doesn't survive being pulled out, rewrite it.<Stats> row, a <Figure>, a <Gallery>.<Split> blocks, a <Divider>, then tighter paragraphs.βWriting is architecture, not decoration. Pick the load-bearing sentence first; arrange the rest around it.
That's the tour. Now go write something.