I develop this blog by Next.js, and I use Notion as editor of the blog, so I write about that.
 
The structure is really simple. Next.js server calls notion API to get posts information from the notion table. The below image is my memo before coding.
notion image
 
 
This is the code of this blog.
 
 
This is my first time writing a tech post in English, so if you have any pointers, whether about programming or English, please send a comment on the notion page below.
 

Contents

  1. Why Next.js
  1. Why Notion
  1. React Notion X
  1. Code
  1. Reflections
 

1. Why Next.js

Next.js is React framework that can be used for developing web applications functionalities such as server-side rendering(SSR) and Static-Site Generating(SSG).
The reason why I used it in this blog system is that it’s really easy to develop web applications including using Vercel. Also, SSG is a good structure to make a light and fast website.
 
We have the option of using SSR, SSG and using both of them by Next.js. SSR renders pages by each request. In contrast, SSG generates pages when updated.
SSR is Subway; you start cooking after ordering, so it is easy to handle even if each person's order is different. It means it’s a better method for sites like Twitter, where everyone posts a lot.
SSG is KFC; you can put the fried chicken up beforehand and serve it when ordered because normally most customers orders basic fried chicken. It means it’s a better method for sites like blogs that don’t update content frequently.
 
Thus, I used SSG in this blog system, and the blog is now light and fast.
 
 

2. Why Notion

I used notion as editor of this blog.
I like the notion: good design and markdowns. However, the biggest reason why I want to use notion as editor of the blog is high-performance database table functions.
I can date information and tag information as hashtags with coloring that makes high visibility. Also, I can filter, sort, and search by using this information on the notion page.
notion image
With notion, I no longer need to create a sophisticated editor that is difficult to create and an administration page to manage the articles I have written.
 
 

3. React Notion X

React Notion X is a wonderful library. For the reason stated in the previous chapter, I decided to use the notion and have come to the conclusion that I would like to display the post on my blog without changing the notion design.
 
Then, I found the perfect library which fulfilled my request.
This library provides some functions. One of them is getting notion page data, and just passing that data to NotionRender, it renders that notion page by the same design.
 
Thanks to the notion and this library, I don't even have to write the design in CSS anymore.
 
 

4. Code

This blog is really simple. Using compornents/notion/index.tsx for get data from notion, then reander at pages/index.tsx and pages/posts/[slug].tsx.
 

compornents/notion/index.tsx

This code has some functions which get data from notion by using Notion API and notion-client which is provided to get page content for React Notion X render.
Some parts are omitted by using “~~~~”.
export const getPosts = async ( databaseId: string) => { const response = await notion.databases.query({ database_id: databaseId, }) const { results } = response let posts = results.map((result:any) => { const d = result.properties // ~~Add slug if it is empty~~ const item:any = { id: result.id, url: result.url, slug: '', title: '', date: '', edited: result.last_edited_time, published: false, hashtags: [], recordMap: {}, } Object.keys(d).forEach((key) => { // https://github.com/masaishi/notion-blog/blob/main/compornents/notion/index.tsx~~Converting data into a form that fits the form~~ }) // ~~Add date if it is empty~~ posts = posts.filter((post): post is Post => typeof post !== 'undefined'); // Sort database by date. posts.sort((a, b) => b!.date - a!.date); return posts } // Get page detail export const getPage = async (pageId: string) => { const page = await notionApi.getPage(pageId); return page } export const updateProperties = async (pageId: string, properties: any) => { const response = await notion.pages.update({ page_id: pageId, properties, }); } export const getHashtags = async () => { const response = await notion.databases.query({ database_id: process.env.NOTION_DATABASE_ID ?? '', }) const { results } = response as any let hashtags:Hashtag[] = [] for(const result of results){ const d = result.properties! as any for(const select of d.Hashtags.multi_select) { if(!hashtags.filter(hashtag => hashtag.name == select.name).length){ hashtags.push({'name':select.name, 'color':select.color, 'count':1}) }else{ hashtags.filter(hashtag => hashtag.name == select.name)[0].count += 1 } } } hashtags = hashtags.sort((a, b) => b.count - a.count); return hashtags }
getPosts is a function to get data from the notion database table.
This page tells me how to use Notion API for Next.js.
Notion APIで自分のポートフォリオサイトを作ってみた
Notionのbeta版のパブリックAPIで現時点でどこまでのことができるか試してみるため、自分のポートフォリオサイトを実際にNotion APIで作ってみました。 この記事ではNotion APIの使い方と、どのようにポートフォリオサイトを作ったかをご紹介したいと思います。 実際に完成したポートフォリオサイトはこちら こちらはその元となっているNotionのページになります。 使用技術は以下のようになります。最近自分の中でNext.jsとTypeScriptは切っても切れない技術になってきてます。 また、@notionhq/client というNotionが公式で提供しているnpmライブラリを使うことで簡単に記事データを取得することができました。 Notionにてデータベースを作る NotionにてSecret KeyとデータベースIDを取得する @notionhq/clientを使って自分のNotionからデータを取得し描画する まずはNotionにてデータベースを作りましょう。/table と入力するとテーブルブロックが作成できますが、これがデータベースに当たります。 次に実際にデータを作っていきます。私の場合は以下のようなプロパティを用意しました。 Page ポートフォリオのタイトル Slug ポートフォリオを表示する際のURLに使用 Published ポートフォリオを表示するかどうか Date ポートフォリオの日付 Authors この記事を書いた人 Thumbnail 記事のサムネイル Description ポートフォリオの簡単な説明 次にNotionにてSecret KeyとデータベースIDを取得します。 まずはSecret Keyを取得しましょう。 NotionのページよりSettings & Members → Integrations → Develop your own integrationsの順に移動していただき、新しいIntegrationを作成することでSecret Keyを取得できます。 次に先ほど作成したテーブルに対して作成したintegrationを招待します。 Notionのの右上にあるShareボタンをクリックし表示されるポップオーバーにて、Inviteより作成したintegrationを招待してください 次にデータベースIDを取得します。 テーブルのページは以下のようなURLになっていて、 ***************** の部分がデータベースIDになるので控えておきます。 最後に@notionhq/client を使って実際にNotionからデータを取得して描画してみましょう。 まずは以下のようにnotionに接続するClientを作ります。 一覧ページ 控えておいたデータベースIDを使って対象のテーブルからレコードを取得します。 かなり雑ですが取得できたデータを以下のような関数で加工して一覧データとして扱いやすい形に変換しています。 最終的にNext.jsで getStaticProps を使って以下のようなイメージでISRで一覧ページを表示しています。 詳細ページ また対象のページのデータを以下のようにClientから取得できます。 対象ページのブロック一覧も以下のようにClientから取得できます。 当ポートフォリオサイトでは各記事のブロック一覧を以下のようにReactとTailwindCSSで表示しています。 @notionhq/client を使えばTypeScriptで書いてにデータベースから取得したデータの型情報を調べることができ、開発体験はとてもよかったです。 ただし、2021年11月6日現在では、まだデータ的に情報が足りていないブロックもいくつかあり、本格的なものを作るにはもう少し待ちたいなと思いました。 テキストや見出し、リスト、画像を使った簡単なブログなら現時点でも十分に作れそうなことがわかりました。 実際のポートフォリオサイトのソースコードはこちらです。 コードはかなり雑ですがご容赦ください。
Notion APIで自分のポートフォリオサイトを作ってみた
This code is described by the upper article. This code which makes a portfolio with next.js and the notion is really helpful.
This code doesn’t use React Notion X, but this code tells me how to get data from the notion database table.
 

pages/index.tsx

After using getPosts to get posts data, it filter posts depends on hashtags and put in show_posts. The below code is one part in pages/index.tsx.
{show_posts.map((post:Post) => ( <div className="card mt-5" key={post.id}> <PostContent post={post} /> </div> ))}
 
PostContent is one compornent in layout.
import React from 'react' import Head from 'next/head' import Image from 'next/image' import Link from 'next/link' import styles from '../styles/Home.module.css' import { NotionRenderer } from 'react-notion-x' import { Post } from '../notion/postType' export const PostContent = ({post}:{post:Post}) => { return ( <div className="card-body" key={`${post.id}_content`}> <div className='notion light-mode notion-page notion-block'> <div className="card-title d-flex align-items-center"> <div className="profile align-items-center"> <Link href={`/posts/${post.slug}`}> <a className="h2 mb-0 notion-link" id={`${post.id}_title`}>{post.title}</a> </Link> </div> </div> <h6 className="card-subtitle mb-2 text-muted" id={`${post.id}_published_at`}>posted_at: {new Date(post.date ?? '').toLocaleString()}</h6> <h6 className="card-subtitle mb-2 text-muted" id={`${post.id}_edited_at`}>edited_at:  {new Date(post.edited).toLocaleString()}</h6> <div className="d-flex"> {post.hashtags?.map((hashtag) => { return ( <div key={`${post.id}_${hashtag}`} className="me-3"> <Link href={`/?hashtags=${hashtag}`}> <p className="h6 mb-0 notion-link">#{hashtag}</p> </Link> </div> ) })} </div> </div> <div className="card-text mt-4 post-content contents-texts"> {/* <div className="card-text post-content contents-texts" id={`${post.id}_contents`}> */} <NotionRenderer recordMap={post.recordMap} fullPage={false} darkMode={false} /> </div> </div> ) }
As you can read, it just passes the post received as an argument to the render of React Notion X
 
 
Only these two codes are big parts of this blog. I think the reason I was able to develop this blog in such a short time was that I was able to find , a code that did almost exactly what I wanted to do.
 
 

5. Reflections

Since I had already created a site with next.js and react-notion-x, I was able to create the blog itself almost in one day, and the below table is how much time I spend to develop
Contents
Time
Render posts at the index page
2h 30m
Detail pages
2h 5m
Hashtag
2h 55m
Others
1h 40m
 
The goal I have established in developing this blog was to finish that within a weekend, and I think using notion and React Notion X was the reason I achieved it.
When developing something, a thinking structure that is easy to develop is just as important as how to code, and I think I came up with a very good way this time, so that was good.
 
However, I have a reflection point on the coding part. I used “any” and “!” many times for passing compile.
I have only worked with dynamically typed languages so far, so I want to learn not only how to write typescript, but also its philosophy and design patterns of statically typed language.
 
To sum up, I need to study typescript and statically typed language more, but I think the blog itself was created just fine. I developed a hashtag filter function, so I can make links to show only English pages or Japanese pages.
In conclusion, both Next.js and notion are great, and I will continue to use them!