This template is currently in progress!
I'm building this Notion as a CMS portfolio + blog template in public.
Follow my daily updates on X
and Bluesky
.
a portfolio website with a blog, built using Next.js with Notion as the CMS
manage all your content (pages, blogs, metadata) directly from your Notion app.
Launching soon, you'll be able to buy this template and use it for your own portfolio & blog.
Interested in buying this template? Message me on
X
or Bluesky
.
Text Customization Annotation
Bold, Italicize, Underline, Link, code
, break, s p a c i ng,
( Keeping semantic tags in mind, the HTML structure is kept minimal — no unnecessary <span>
or <div>
clutter. Links are rendered using the <a>
tag, and code snippets use the <code>
tag for better semantic structure. Everything is styled to closely match the look and feel of a Notion page. )
Text Colors & Backgrounds
Default, Gray, Brown, Orange, Yellow, Green, Blue, Purple, Pink, Red.
Default, Gray, Brown, Orange, Yellow, Green, Blue, Purple, Pink, Red.
Text Links
Color Link
Default, Gray, Brown, Orange, Yellow, Green, Blue, Purple, Pink, Red.
Background Link
Default, Gray, Brown, Orange, Yellow, Green, Blue, Purple, Pink, Red.
External Links:
This link navigates to the Notion website
You can add any external website URL from your Notion page. These links open in a new tab using the default <a>
tag with target="_blank"
and rel="noopener noreferrer"
for secure external navigation. It prevents the new tab from being able to control or access your original page, which helps protect your site from certain types of attacks.
Internal Site Links:
This link navigates to the blog page isn’t this great? With this, so many things can open seamlessly!
( These are links to other pages within your site. They use Next.js’s Link
component to enable fast client-side navigation and improved performance. When clicked, these links do not open in a new tab. )
Underline customizations
Default, Gray, Brown, Orange, Yellow, Green, Blue, Purple, Pink, Red.
( you can see here that the underline border inherits text color from the inline code )
Default, Gray, Brown, Orange, Yellow, Green, Blue, Purple, Pink, Red.
Strikethrough customizations
Default, Gray, Brown, Orange, Yellow, Green, Blue, Purple, Pink, Red.
( you can see here that the underline border inherits text color from the inline code )
Default, Gray, Brown, Orange, Yellow, Green, Blue, Purple, Pink, Red.
Inline Code all customizations
Default inline Code
inline code
Color text inline Code
Default
, Gray
, Brown
, Orange
, Yellow
, Green
, Blue
, Purple
, Pink
, Red
.
Background inline Code
Default
, Gray
, Brown
, Orange
, Yellow
, Green
, Blue
, Purple
, Pink
, Red
.
Different Backgrounds Code side by side
Yellow
Green
Blue
Default
Gray
Brown
Orange
Yellow
Green
Blue
Purple
Pink
Red
.
Different Text Colors inline inline Code side by side
Yellow
Green
Blue
Default
Gray
Brown
Orange
Yellow
Green
Blue
Purple
Pink
Red
inline code with Bold
Bold
inline code with Italicize
Italicize
inline code with Underline
Default
, Gray
, Brown
, Orange
, Yellow
, Green
, Blue
, Purple
,Pink
, Red
inline code with Strike Through
Default
, Gray
, Brown
, Orange
, Yellow
, Green
, Blue
, Purple
,Pink
, Red
inline code with Equation
inline code with Link inside
inline code with Emoji inside
const status = "
Success";
console.log("
Debug mode enabled");
const emoji = "
+
=
";
mix inline code with all annotations
The Best Part of All
You can clearly see that these emojis look different, they’re not the default system emojis.
These are Twemojis from X (formerly twitter), styled specifically for the web.
I was able to achieve this by converting the default emojis in the text into Twemojis with this twemoji-parser.
every twemoji supported with skin variation and more
People with skin tone variations
"", "
", "
", "
", "
"
"
", "
", "
", "
", "
"
"
", "
", "
", "
", "
"
"
", "
", "
", "
", "
"
Gendered professions
"", "
", "
" // Developer
"
", "
", "
" // Astronaut
"
", "
", "
" // Artist
Popular and expressive
"", "
", "
", "
", "
", "
", "
", "
", "
", "
", "
"
Aesthetic emojis for web UI
"", "
", "
", "
", "
", "
", "
", "
", "
", "
"
Paragraph Blocs all customizations
Plain Text hi there
Color Paragraph you can any color from notion it will exactly replicated
Background Paragraph you can any color from notion it will exactly replicated
blue color
blue background
brown color
brown background
default color
default background
gray color
gray background
green color
green background
orange color
orange background
pink color
pink background
purple color
purple background
red color
red background
yellow color
yellow background
Lorem ipsum dolor sit, Emojis
ִֶָ☾♡
using https://github.com/jdecked/twemoji-parser
, Bold, Italicize, Underline, My Portfolio link,
Code
, Break, S p a c i n g. Equations: blue color blue background Link site page referring link World Emoji Day
World Emoji Day
yooo hie lal a alsd v asdh
code1
code2
yo
_____________
Also note, I have added Dark Mode support you can toggle it by clicking the Sun button in the top right corner.
Every color looks different in Light and Dark Exact replication of Notion Rich Text customization.
____________
Heading 1
Heading 2
Heading 3
Toggle heading 1
Share this project with people who love Notion.
Toggle heading 2
Hi
Toggle heading 3
Share this project with people who love Notion.
Rich Text also supported in all heading 1, 2, 3 and toggle heading 1, 2, 3 blocks also
Heading 1 , Italicize, Underline, Link, code
, break, s p a c i ng, code1
code2
blue color blue background
Heading 1 block with background blue color blue background
Heading 1 block with color blue color blue background
You can see above even if a block has a background or color, any text inside it that has its own background or color still shows up just like in Notion. This works thanks to the RichText component.
Heading 2 , Italicize, Underline, Link, code
, break, s p a c i ng, code1
code2
blue color blue background
Heading 2 block with background
Heading 2 block with color
Heading 3 , Italicize, Underline, Link, code
, break, s p a c i ng, code1
code2
blue color blue background
Heading 3 block with background
Heading 3 block with color
_______
Bulleted List Item
Notion colors & backgrounds
- default background
- gray background
- brown background
- orange background
- yellow background
- green background
- blue background
- purple background
- pink background
- red background
- default color
- gray color
- brown color
- orange color
- yellow color
- green color
- blue color
- purple color
- pink color
- red color
- Boil fresh water
- Add coffee grounds to your filter or French press
- Pour hot water over the coffee
- Let it brew (about 4 minutes for French press)
- Press or filter the coffee
- Pour into your favorite mug
- Add milk or sugar if desired
- Enjoy your coffee break!
Numbered list Item
- 1.One
- 1.Two
- 1.Three
- 1.Four
- 1.Five
- 1.Six
- 1.Seven
- 2.One
- 1.Two
- 1.Three
- 2.Four
- 1.Five
- 2.Six
- 3.Lorem ipsum dolor sit amet
- 4.Example of rich text with a long paragraph:
Here’s a more complex example with long text inside the numbered list:
- 1.Main Topic
- 1.Sub Topic
- 1.Details
- 2.Further Explanation
- 1.Additional information with rich formatting:
- Bold: Important
- Italics: Emphasized
- 2.A long paragraph: Lorem ipsum dolor sit amet consectetur adipisicing elit. Ipsa enim dolor illo officia, voluptas facilis fuga ut recusandae quam nulla iure. In, laborum quibusdam!
- 1.Wake up and stretch
- 2.Drink a glass of water
- 3.Make your bed
- 4.Take a shower
- 5.Brew some coffee or tea
- 6.Eat a healthy breakfast
- 7.Review your to-do list
- 8.Start your first task
Bulleted List Item and Numbered List Item Rendering
I was able to achieve rendering that exactly matches Notion’s visual and structural style.
- List item blocks Groups:
The Notion API provides list items blocks individually, So I group adjacent
bulleted_list_item
andnumbered_list_item
blocks into a single block and pass them to their respective components. - Semantics:
Each list is wrapped in a
<ul>
or<ol>
, and each item is rendered using an<li>
element, maintaining correct HTML semantics. - Bullets:
Bulleted lists (
<ul>
) automatically adjust bullet styles based on nesting depth. The style alternates between disc, circle, and square, just like Notion. - Numbering:
Numbered lists (
<ol>
) automatically adjust numbering based on nesting depth. The numbering style alternates between numbers, letters, and Roman numerals depending on the level, just like Notion. - Styling:
Custom background and text colors for list items are rendered exactly as in Notion. Rich text formatting (bold, italic, links, etc.) is also fully supported inside each list item.
- Children:
Nested list items and child blocks are correctly rendered within their parent list item, preserving hierarchy and structure.
Synced Block
this block is synced from blog page
Blog Home Page
Heading 1
Heading 2
Heading 3
Toggle heading 1
Share this project with people who love Notion.
Toggle heading 2
Hi
Toggle heading 3
Share this project with people who love Notion.
Paragraph
- Bulleted list item
- one
- two
- 1.Numbered list item
- 2.one
- 3.two
Toggle block
Toggle block
HI
Empty Toggle block
Background Toggle Block
Hi
Color Toggle Block
Hi
To do Block
To-do item (unchecked)
To-do item (checked)
color to do (checked todo text will not have block color but you can add text color by selecting individual text like this also strike through color can be change like this)
background to do (checked)
unchecked
color to do (unchecked)
background to do (unchecked)
This is a paragraph inside an unchecked to-do.
Toggle inside a to-do
- Bulleted list item
- Hi
- 1.Numbered item
- 2.Hi
Divider block
Quote block
To be or not to be, that is the question.
— William Shakespeare
Quote Background color
Simplicity is the ultimate sophistication.
(This quote block has a custom background color.)
Quote Text Color
Stay hungry, stay foolish.
(Text appears in brown in Notion)
Quote with child blocks
Heading 1 inside Quote
Some supporting paragraph text.
A bullet list item Another oneEven a nested quote block!
Columns Block
Supports all column layouts: 1, 2, 3, 4, and 5 columns.
This block in Notion is used to organize content side by side, perfect for layouts that need structure and flexibility.
It also accurately reflects custom column widths if you change a column’s width in Notion, it will render with the exact same width here.
Big thanks to the Notion API for providing detailed column width data
2 Columns
Column 1
Column 2
3 Columns with Custom Widths
Column 1
Column 2
Column 3
4 Columns
Column 1
Column 2
Column 3
Column 4
5 Columns
Column 1
Column 2
Column 3
Column 4
Column 5
Nested columns? Yes! You can even place a Columns block inside a column — just like Notion allows.
Column 1
Column 1
Column 2
Column 2
Audio Block
File Block
Figma Keyboard Shortcuts for Windows.pdf
1636 KBdummy.pdf
Handling Notion-hosted and External Files in File & Audio Blocks
In both the File and Audio blocks, we receive either Notion-hosted URLs (files uploaded directly to Notion) or externally hosted URLs (e.g., S3, Dropbox, or public CDN links).
Now, Notion-hosted URLs are temporary, they typically expire after 1 hour. This means if we directly use these URLs in our custom Notion block renderer, the file or audio will stop working shortly after being loaded.
To address this, I implemented a workaround:
- For Notion-hosted audio block files, we download them during build time and store them in
/public/audio/
. - For Notion-hosted file block files, we store them in
/public/file/
. - For audio files, we extract the exact filename and format from the Notion URL and save it accordingly.
- For file blocks, we use the filename provided in the Notion API (
block.file.name
) and preserve it when saving.
I’ve also added a check to avoid redundant downloads, if a file already exists in the respective folder, we reuse it instead of downloading it again.
Although this approach increases the project’s build size, it ensures long-term availability of files without relying on expiring Notion URLs.
I'm also considering uploading these Notion-hosted files to a CDN or cloud storage instead of storing them in the Next.js
public
folder, to reduce the overall project size.
Code Block
Code block with with notion rich text
JavaScript code block
function debounce(func, delay) { let timer; return function (...args) { clearTimeout(timer); timer = setTimeout(() => func.apply(this, args), delay); }; }
// Example usage:
const fetchData = () => { console.log("Fetching data..."); };
const debouncedFetch = debounce(fetchData, 1000);
document.getElementById("search").addEventListener("input", debouncedFetch);
TypeScript code block
import React from 'react';
// A simple functional component that returns "Hello, World!"
const HelloWorld: React.FC = () => {
return (
<div>
<h1>Hello, World!</h1>
</div>
);
};
// Component with a sum function that calculates the sum of two numbers
const SumComponent: React.FC = () => {
const sum = (a: number, b: number): number => {
return a + b;
};
return (
<div>
<h2>The sum of 5 and 10 is: {sum(5, 10)}</h2>
</div>
);
};
// Main App component
const App: React.FC = () => {
return (
<div>
<HelloWorld />
<SumComponent />
</div>
);
};
export default App;
Mermaid
%% A simple Mermaid diagram
graph TD;
A[Hello, World!] --> B{Sum};
B -->|5| C[5];
B -->|10| D[10];
C --> E[Result];
D --> E;
graph TD;
A[User] -->|Enters URL| B[Browser]
B -->|Sends HTTP Request| C[DNS Resolver]
C -->|Resolves Domain to IP| D[Web Server]
D -->|Processes Request| E{Static or Dynamic Content?}
E -->|Static| F[Serve Static Files]
E -->|Dynamic| G[Application Server]
G -->|Fetches Data| H[Database]
H -->|Returns Data| G
G -->|Generates HTML| I[Web Server]
F -->|Sends Response| J[Browser]
I -->|Sends Response| J
J -->|Parses HTML| K[DOM Construction]
K -->|Loads Resources| L[CSS, JS, Images]
L -->|Executes JS| M[Render Page]
M -->|Displays Content| N[User Sees Website]
flowchart LR
id1>This is the text in the box]
erDiagram
CUSTOMER }|..|{ DELIVERY-ADDRESS : has
CUSTOMER ||--o{ ORDER : places
CUSTOMER ||--o{ INVOICE : "liable for"
DELIVERY-ADDRESS ||--o{ ORDER : receives
INVOICE ||--|{ ORDER : covers
ORDER ||--|{ ORDER-ITEM : includes
PRODUCT-CATEGORY ||--|{ PRODUCT : contains
PRODUCT ||--o{ ORDER-ITEM : "ordered in"
flowchart TD
A[Start] --> B[Process]
B --> C{Decision}
C -->|Yes| D[Result 1]
C -->|No| E[Result 2]
flowchart TD
Mermaid:::frenzy-->NonBreakableSpace-->ReportTo --> NotionHQ & MermaidOnGithub
MermaidOnGithub-->GitHub:::frenzy
Mermaid-->Mermaids_FlowchartsArticle:::linked_frenzy-->SpecialChars:::linked
Diagrams.net--> GitHub
Mermaid-->DiagramTypes-->ZILLIONSOFTANGENTS["ZILLIONS OF TANGENTS #129325;"]:::frenzy-->ZILLIONSOFTANGENTS
Mermaids_FlowchartsArticle-->Share-->Tw_Notion & Tw_Nerd
Mermaid-->Yt_Nutt_FlowchartGenerator["William Nutt's<br/>Flowchart Generator<br/>."]:::linked
Mermaids_FlowchartsArticle-->WebSrch_YedToMermaid
MiscTangents:::frenzy--> GitHub & CSS & AS-ADHD & Datamining
WebSrch_YedToMermaid:::frenzy-->Diagrams.net:::frenzy
GitHub-->GitLab & MediaProduction_InGit
MediaProduction_InGit-->MediaProduction_Tools-->MediaProduction_InCloud & Linux & VFX & AWS & Deep & OBS
VFX-->Blender & AfterEffects_Alternatives