Content Architecture: TSX-First with markdown Fallback
January 20, 2026
Why TSX is the first-class citizen and Markdown is just an input format
Philosophy
TSX is the first-class citizen. Markdown is just an input format, not a working format.
We write 95% TSX. Content and logic are inherently coupled in this paradigm. Instead of fighting that reality, we embrace it.
The System
File Priority
- Check for
.tsxversion → use if exists (preferred) - Fall back to
.md→ parse with react-markdown + custom components - No file → 404
Directory Structure
/content/
my-post.md <- fallback, gets auto-rendered
my-post.tsx <- preferred, full control
/source/raw/assets/
[markdown files live here for now]Rendering Strategy
MD files get parsed with custom component mapping:
const components = {
h1: MyH1Component,
p: MyParagraph,
code: MyCodeBlock,
// etc
}
<ReactMarkdown components={components}>
{mdContent}
</ReactMarkdown>When we need custom logic, interactivity, or special components → create the TSX version.
Why Not MDX?
MDX assumes markdown-first, JSX-second.
Our reality is TSX-first, markdown-as-input.
We don't want to make markdown do things it wasn't meant to do. We use markdown as raw content that gets promoted to first-class TSX when needed.
Content Storage: In-Repo for Now
Currently: 4 MD files in /source/raw/assets/
Why in-repo:
- Version control for content + code together
- Simple deployment (everything in one place)
- No external dependencies, APIs, or auth
- Fast local development
When to move out of repo:
- Non-technical people need to edit (need a UI)
- Hundreds/thousands of files bloating the repo
- Build times suffering
- Multi-repo content sharing needed
We'll know when we hit those thresholds. Until then: keep it simple.
Development Philosophy
No Premature Factoring
Create → Iterate → Improve → Factor when painful
Don't abstract until:
- Pattern appears 3+ times
- Every change requires multiple edits
- The pain is real and felt
The code will tell you when it wants to be factored.
Why This Works
Early factoring means guessing at patterns that don't exist yet. You don't know:
- What gets reused
- What changes together
- What the actual constraints are
Living with the "mess" for a bit means when you DO factor, the abstraction fits perfectly instead of being a generic guess.
Implementation Notes
- Using react-markdown (or similar) for MD parsing
- Custom components map to all markdown elements
- TSX files have full access to all repo components, logic, types
- MD files are treated as content ingestion points (like copying a conversation into the repo)
- Everything renders through the same component system
Update: January 20 2026
The directory structure has evolved since this was originally written:
/content/
tsx/ <- TSX posts (preferred)
md/ <- Markdown posts (fallback)
/src/
app/
components/
lib/Content files now live in /content/tsx/ and /content/md/directories at the root level, separate from the source code. The philosophy remains the same: TSX is first-class, markdown is a fallback format.
Related Posts
- Why My Blog Posts Are Programs, Not DocumentsExploring why my blog posts are written as code instead of simple markdown files, and the tradeoffs that come with this approach.