It’s that time again! Time to do some spring cleaning on the blog site and check out the latest and greatest in front-end tech. There’s been some great progress in static site frameworks, so let’s chat about my latest journey.
Updated Blog
Framework: Next.js 13
CSS: TailwindCSS
CMS: Notion
Deployment: Vercel
Repo: Updated Site Repo
As this is a technical blog, let’s go into a little more detail about the implementation.
Next JS
I’ve been following the progress of Next for a while now and I can say, after implementing it myself, that it’s my new favorite static site framework. I am using version 13, but I’m not using some of the newer features such as the new /app directory or Turbopack (the rust-based successor to Webpack) until they come out of beta.
Notion as a CMS
I’m a huge Notion Junkie.
I organize all my daily tasks, projects, notes, and keep several databases for movies, TV, and readings all in Notion. They recently opened their API to the public which is how it can be used as a CMS. It’s not as fully featured as some CMS’s, but it still works great. There are a few ways to get this going, so I’ll go over a few.
Setting up Notion Database
In Notion I just setup a database with the following schema:
Option 1: Rendering with React Notion X
This library fetches data on a “Page” level and renders an exact clone of what it looks like in Notion. There’s not a whole lot of flexibility for styling, but it’s a quick and easy way to add content to an external site.
import { NotionAPI } from 'notion-client'
/*
Chrome Development Tools > Application > Cookie > and Copy the
token_v2 and notion_user_id
*/
const notion = new NotionAPI({
activeUser: process.env.NOTION_ACTIVE_USER,
authToken: process.env.NOTION_TOKEN_V2
})
// Page ID grabbed from the url
const recordMap = await notion.getPage('067dd719a912471ea9a3ac10710e7fdf')
...
import * as React from 'react'
import { NotionRenderer } from 'react-notion-x'
export default ({ recordMap }) => (
<NotionRenderer recordMap={recordMap} fullPage={true} darkMode={false} />
)Option 2: Rendering with Notion as Markdown (What I used)
For this approach, you’ll need to use the Notion official API client to download the page, convert it to markdown using the Notion to Markdown library, then use React Markdown to render the data.
// Start the notion client
const notion = new Client({
auth: process.env.NOTION_TOKEN,
});
// Fetch all published posts from the database id
export const getAllPublished = async (): Promise<PostMetadata[]> => {
const posts = await notion.databases.query({
database_id: process.env.DATABASE_ID || '',
filter: {
property: "Published", // Only grab entries that are published
checkbox: {
equals: true,
},
},
sorts: [
{
property: "Date",
direction: "descending",
},
],
});
return posts.results;
};
// Fetch a single post and convert it to markdown
const n2m = new NotionToMarkdown({ notionClient: notion });
export const getSingleBlogPostBySlug = async (slug: string): Promise<SinglePost> => {
const response = await notion.databases.query({
database_id: process.env.DATABASE_ID || '',
filter: {
property: "Slug",
formula: {
string: {
equals: slug,
},
},
},
});
const page: any = response.results[0];
const metadata = getPageMetaData(page);
const mdblocks = await n2m.pageToMarkdown(page.id);
const mdString = n2m.toMarkdownString(mdblocks);
metadata.cover = page.cover.external.url;
return {
metadata,
markdown: mdString,
};
};
...
// Not every markdown tag is defined, so you’ll need to override using the
// React Markdown settings.
<ReactMarkdown
components={{
// When the code tag is used, replace with custom code component with highlighting
code({ node, inline, className, children, ...props }) {
const match = /language-(\w+)/.exec(className || '')
return !inline && match ? (
<CodeBlock
codestring={String(children).replace(/\n$/, '')}
language={match[1]}
/>
) : (
<code className={className} {...props}>
{children}
</code>
)
}
}}>
{markdown}
</ReactMarkdown>There are plenty more options, but these are what I determined to be the “best” in my own opinion.
TailwindCSS
I’ve been creating and organizing my own CSS/SCSS files for years now, so I thought it was time to try something out-of-the-box. I know there are plenty of frameworks and design systems to choose from, but I decided to see what all the buzz was about with Tailwind.
The big feature that caught my attention was it’s use of utility classes instead of semantic class names. Basically, for (almost) every CSS property, they’ve created a class that implements it. For instance if you want an element to have rounded edges you just add the class rounded or the other preset radius classes and now the element will be rounded. You can compose multiple classes to style specific elements. For example:
Vanilla
.header {
padding: 60px;
text-align: center;
background: #000;
color: white;
font-size: 30px;
}
<h1 class="header">Heading 1</h1>Tailwind
<h1 class="p-10 text-center bg-black text-white text-3xl">Heading 1</h1>It can definitely add some clutter to the HTML but it decreases complexity by having only one file. All the settings and definitions can be customized so you aren’t tied into their designs. Implementing styling this way can also cause repetition in your code. A good thing though, is that it forces the code to be split up into reusable components, which is good design anyways.
All in all, even though it takes a little while to get used to, I think it’s worth it in the end.
Future Features
I still haven’t implemented all of the features that I wanted, but here’s my wish list: