1. Overview
A Notion CMS blog uses Notion as the writing and content management tool, while the public-facing site is rendered by a separate web app. You get to keep Notion's editing experience while taking full control over design, routing, search, and deployment — combining the writing convenience of existing blog platforms with the freedom of a custom-built blog.
Various blog services (Github Pages, Tistory, Medium, Velog)
Like many people, I had wanted a blog of my own to organize what I studied ever since my university days. I tried Github Pages and Tistory, but the following limitations always got in the way.
| Platform | Pain points |
|---|---|
| Github Pages | Pros: easy to customize / Cons: writing workflow is cumbersome and the editor is inconvenient |
| Tistory | Pros: built-in editor and basic features / Cons: hard to customize, difficult to build the features I wanted |
Because writing itself was such a hassle, I gradually stopped writing, and my blog went dormant. Years later, while working on personal studies and projects, I realized I needed a place to record things systematically and revisit them later.
Since I already used Notion heavily for organizing notes, a Notion-based blog was the natural idea. However, simply publishing Notion pages made it impossible to customize the design or features. So I decided to use Notion as a CMS (Content Management System) and build the design and features myself.
TIP
To overcome the limitations of existing blog platforms, I chose to build my own blog using Notion as a CMS.
2. Design / Planning
Simple publishing couldn't offer category/tag search or other convenience features, so I decided to keep Notion as the CMS and handle everything from planning to development myself. I worked on the planning in spare moments after work, and it took about a month.
Before designing components, when choosing the blog's color scheme, I built a color palette with v0.dev and asked friends for advice (I picked the background color myself).
Color palette drawn with v0.dev
After that, I designed in a bottom-up structure, starting from the smallest components (Typography, Button, etc.) and expanding outward, iterating on revisions based on my friends' feedback.
NOTE
Nailing down the color scheme and component-level design first, then expanding, helps speed up development later on.
Blog component designs I drew myself in Figma
Page designs built from the components
3. Development / Implementation
I built this project in a Vibe Coding style using Figma MCP + Github Copilot.
Figma and Github Copilot
Just by feeding in the URL of a component designed in Figma (some setup required), Github Copilot would write the component code, Storybook stories, and test code for me.
├── PostRow
│ ├── PostRow.stories.tsx
│ ├── PostRow.test.tsx
│ ├── PostRow.tsx
│ └── index.tsx
└── TagList
├── TagList.stories.tsx
├── TagList.test.tsx
├── TagList.tsx
└── index.tsxThat said, as someone with limited FE experience, the questions never stopped even while Vibe Coding.
"Should I design the Props now or later? Should I build the design elements first? When should I add dark mode? Should I implement the Notion data logic first?"
I just dove in, going through countless trials, errors, and rounds of refactoring. Since AI-written code sometimes behaves differently from what you intended — an inherent trait of Vibe Coding — I put real effort into the CI environment to maintain quality.
3.1. Mandatory Storybook & Test Code
I specified in copilot-instructions.md that "every component implementation must include Storybook and test code," and used codecov.yml to target a test coverage of 80% or higher.
Example coverage report
3.2. Automated Testing with Github Actions
I configured Github Actions to automatically run tests and builds whenever a PR is opened. Only when the build, tests, and static analysis all pass can anything merge into main — preventing the worst-case scenario of broken code getting merged.
Github Checks items
3.3. Package Management with Dependabot
I defined update rules and auto-merge behavior in dependabot.yml to keep dependencies regularly updated. This prevented libraries from going stale and allowed security patches to be applied quickly.
Automatic dev package updates with dependabot + auto-merge
4. Deployment
Deployment was something I mulled over for a long time. At first I considered deploying to my own server, but a static build was all the blog needed — no server required. So I went with Github Pages, converting Notion posts into static output at build time and deploying that.
However, every time I ran a build, a timeout occurred while fetching Notion data. I couldn't find the cause for a while, but after looking into react-notion-x's Notion page fetch behavior and related reports, I found context suggesting that fetches can hang on pages containing a collection/database.
NOTE
(As of 2025.09.29) The fetch issue was fixed in react-notion-x version 7.6 and no longer occurs.
Downgrading react-notion-x to a specific version as suggested made the build complete successfully, and I was finally able to deploy my self-built Notion CMS blog.
WARNING
When choosing static build deployment, it's important to verify the build-time behavior of the libraries you use in advance.
5. Wrap-up / Retrospective
I initially thought it would take two months, but with planning dragging on and personal matters piling up, it ended up taking four. Still, I felt proud to have finished and deployed it in spare moments after work. I completed one of my two side-project goals for the year, and my first React-based FE project gave me the chance to think about better processes and structure.
There are still features left to build (someday).
- About page
- ToC (Table of Contents)
- Fuzzy Search
Finally, I want to thank my friends who generously gave feedback on every bit of the planning, design, and features.
TIP
Vibe Coding boosts productivity, but design decisions and CI setup were still the developer's job. For my next project, I plan to invest more time in the initial design phase.