Nextjsと概念APIによって供給されるブログを構築してください
56086 ワード
導入
それは私の個人的な生活になるの概念ゲームチェンジャーされています.それは私の目標を文書化からすべてを管理することができます私の考えをジャーナリング.このため、私は概念を使用して私の個人的なブログのWordPressのようなツールを介してこれまでの概念を残していないの利便性の電源を使用します.このチュートリアルでは、NextJS & TailWindCSSと連動して、あなたのブログに電源を入れるために、どのようにNotionAPIを使用できるかを示します.
設定の概念
あなたがこのチュートリアルのために彼らの自由な層を使うことができる点に注意してください.
概念統合の作成
移動するhttps://www.notion.so/my-integrations と新しい内部の統合を作成する
データベースの作成
テンプレートを複製することができますhere .
ブログへのグラント統合アクセス
共有ボタンをクリックし、統合アクセスを行います.
プロジェクト作成
アプリケーションを作成する
$ npx create-next-app mysite --typescript
インストールする
npm install -D tailwindcss postcss autoprefixer @tailwindcss/typography
npx tailwindcss init -p
セットアッププロジェクト
を設定します
あなたの
tailwind.config.js
ファイルを追加し、次のように追加します.module.exports = {
content: [
"./pages/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
fontFamily: {
sans: ["'Montserrat'"],
mono: ["'Inconsolata'"]
}
},
plugins: [
require('@tailwindcss/typography')
],
}
グローバル風にCookieを追加します.CSSファイル
@tailwind base;
@tailwind components;
@tailwind utilities;
ドキュメントを追加します.TSX
カスタムフォントを使用するには、新しいファイルを作成する必要があります
pages/_document.tsx
を参照してくださいimport Document, {Html, Head, Main, NextScript, DocumentContext} from 'next/document'
class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext) {
const initialProps = await Document.getInitialProps(ctx)
return {...initialProps}
}
render() {
return (
<Html>
<Head>
<link rel="preconnect" href="https://fonts.googleapis.com"/>
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin={'true'}/>
<link
href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@200;300;400;500;600;700;800;900&family=Montserrat:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"
rel="stylesheet"/>
</Head>
<body>
<Main/>
<NextScript/>
</body>
</Html>
)
}
}
export default MyDocument
追加.envファイル
新しいファイルを作成する
.env.local
次の情報を使用します.NOTION_ACCESS_TOKEN=
NOTION_BLOG_DATABASE_ID=
のためにNOTION_ACCESS_TOKEN
我々は統合し、秘密キーをコピーに行くことができますのために
NOTION_BLOG_DATABASE_ID
UUIDをURL内でコピーできますタイプファイルの追加
新しいファイルを作成する
@types/schema.d.ts
次の行を追加します.export type Tag = {
color: string
id: string
name: string
}
export type BlogPost = {
id: string;
slug: string;
cover: string;
title: string;
tags: Tag[];
description: string;
date: string
}
プロジェクトのビルド
クライアントをインストール
私たちは、ブログのデータとディスプレイ用の他のパッケージを得るために、JavaScriptクライアントという概念をインストールする必要があります
npm install @notionhq/client notion-to-md react-markdown
カスタム概念サービス
import {Client} from "@notionhq/client";
import {BlogPost, PostPage} from "../@types/schema";
import {NotionToMarkdown} from "notion-to-md";
export default class NotionService {
client: Client
n2m: NotionToMarkdown;
constructor() {
this.client = new Client({ auth: process.env.NOTION_ACCESS_TOKEN });
this.n2m = new NotionToMarkdown({ notionClient: this.client });
}
async getPublishedBlogPosts(): Promise<BlogPost[]> {
const database = process.env.NOTION_BLOG_DATABASE_ID ?? '';
// list blog posts
const response = await this.client.databases.query({
database_id: database,
filter: {
property: 'Published',
checkbox: {
equals: true
}
},
sorts: [
{
property: 'Updated',
direction: 'descending'
}
]
});
return response.results.map(res => {
return NotionService.pageToPostTransformer(res);
})
}
async getSingleBlogPost(slug: string): Promise<PostPage> {
let post, markdown
const database = process.env.NOTION_BLOG_DATABASE_ID ?? '';
// list of blog posts
const response = await this.client.databases.query({
database_id: database,
filter: {
property: 'Slug',
formula: {
text: {
equals: slug // slug
}
},
// add option for tags in the future
},
sorts: [
{
property: 'Updated',
direction: 'descending'
}
]
});
if (!response.results[0]) {
throw 'No results available'
}
// grab page from notion
const page = response.results[0];
const mdBlocks = await this.n2m.pageToMarkdown(page.id)
markdown = this.n2m.toMarkdownString(mdBlocks);
post = NotionService.pageToPostTransformer(page);
return {
post,
markdown
}
}
private static pageToPostTransformer(page: any): BlogPost {
let cover = page.cover;
switch (cover) {
case 'file':
cover = page.cover.file
break;
case 'external':
cover = page.cover.external.url;
break;
default:
// Add default cover image if you want...
cover = ''
}
return {
id: page.id,
cover: cover,
title: page.properties.Name.title[0].plain_text,
tags: page.properties.Tags.multi_select,
description: page.properties.Description.rich_text[0].plain_text,
date: page.properties.Updated.last_edited_time,
slug: page.properties.Slug.formula.string
}
}
}
インデックスファイル
最初に、私たちは
staticProps
方法:import {GetStaticProps, InferGetStaticPropsType} from "next";
import Head from "next/head";
import {BlogPost} from "../@types/schema";
import NotionService from "../services/notion-service";
export const getStaticProps: GetStaticProps = async (context) => {
const notionService = new NotionService();
const posts = await notionService.getPublishedBlogPosts()
return {
props: {
posts
},
}
}
const Home = ({posts}: InferGetStaticPropsType<typeof getStaticProps>) => {
const title = 'Test Blog';
const description = 'Welcome to my Notion Blog.'
return (
<>
<Head>
<title>{title}</title>
<meta name={"description"} title={"description"} content={description}/>
<meta name={"og:title"} title={"og:title"} content={title}/>
<meta name={"og:description"} title={"og:description"} content={title}/>
</Head>
<div className="min-h-screen">
<main className="max-w-5xl mx-auto relative">
<div className="h-full pt-4 pb-16 px-4 md:px-0 mx-auto">
<div className="flex items-center justify-center">
<h1 className="font-extrabold text-xl md:text-4xl text-black text-center">Notion + NextJS Sample Blog</h1>
</div>
<div className="mt-12 max-w-lg mx-auto grid gap-5 lg:grid-cols-2 lg:max-w-none">
{posts.map((post: BlogPost) => (
<p key={post.id}>Blog Post Component Here: {post.title}</p>
))}
</div>
</div>
</main>
</div>
</>
)
};
export default Home;
ブログカードコンポーネント
次に、ブログカードのコンポーネントを作成します
最初に日付をモーフィング日付のインストール
$ npm install dayjs
ファイルを作成するcomponents/BlogCard.tsx
import {FunctionComponent} from "react";
import Link from "next/link";
import {BlogPost} from "../@types/schema";
import dayjs from 'dayjs'
type BlogCardProps = {
post: BlogPost
}
const localizedFormat = require('dayjs/plugin/localizedFormat');
dayjs.extend(localizedFormat)
const BlogCard: FunctionComponent<BlogCardProps> = ({post}) => {
return (
<Link href={`/post/${post.slug}`}>
<a className="transition duration-300 hover:scale-105">
<div key={post.title} className="flex flex-col rounded-xl shadow-lg overflow-hidden">
<div className="flex-shrink-0">
<img className="h-64 w-full object-fit" src={post.cover} alt="" />
</div>
<div className="flex-1 bg-gray-50 pt-2 pb-6 px-4 flex flex-col justify-between">
<div className="flex-1">
<span className="block mt-2">
<h4 className="text-xs font-medium text-gray-600">{dayjs(post.date).format('LL')}</h4>
</span>
<span className="block mt-2">
<h3 className="text-xl font-semibold text-gray-900">{post.title}</h3>
</span>
<span className="block mt-2">
<p className="text-sm text-gray-600">{post.description}</p>
</span>
<span className="block mt-2 space-x-4">
{
post.tags.map(tag => (
<span key={tag.id} className='bg-green-300 text-green-800 px-2 py-1 text-xs rounded-lg'>
#{tag.name}
</span>
))
}
</span>
</div>
</div>
</div>
</a>
</Link>
);
};
export default BlogCard;
置換<p>Blog Post Component Here: {post.title}</p>
withimport BlogCard from "../components/BlogCard";
<BlogCard key={post.id} post={post}/>
インデックスファイルで.ポストファイル
次に、1つのブログ記事を表示するページを作成します
post/[slug].tsx
ここで我々は動的パラメータを作ります.💡 両方を利用することになります
getStaticPaths
and getStaticProps
これは、静的なパスを生成しているときに、概念を変更するたびに、サイトを再配備しなければならないことを意味します.import {GetStaticProps, InferGetStaticPropsType} from "next";
import ReactMarkdown from "react-markdown";
import Head from "next/head";
import NotionService from "../../services/notion-service";
const Post = ({markdown, post}: InferGetStaticPropsType<typeof getStaticProps>) => {
return (
<>
<Head>
<title>{post.title}</title>
<meta name={"description"} title={"description"} content={post.description}/>
<meta name={"og:title"} title={"og:title"} content={post.title}/>
<meta name={"og:description"} title={"og:description"} content={post.description}/>
<meta name={"og:image"} title={"og:image"} content={post.cover}/>
</Head>
<div className="min-h-screen">
<main className="max-w-5xl mx-auto relative">
<div className="flex items-center justify-center">
<article className="prose">
<ReactMarkdown>{markdown}</ReactMarkdown>
</article>
</div>
</main>
</div>
</>
)
}
export const getStaticProps: GetStaticProps = async (context) => {
const notionService = new NotionService()
// @ts-ignore
const p = await notionService.getSingleBlogPost(context.params?.slug)
if (!p) {
throw ''
}
return {
props: {
markdown: p.markdown,
post: p.post
},
}
}
export async function getStaticPaths() {
const notionService = new NotionService()
const posts = await notionService.getPublishedBlogPosts()
// Because we are generating static paths, you will have to redeploy your site whenever
// you make a change in Notion.
const paths = posts.map(post => {
return `/post/${post.slug}`
})
return {
paths,
fallback: false,
}
}
export default Post;
回収する
結論では、概念はあなたのCMSのアプリケーションを置き換えるために使用できる強力なツールです.あなたがこのチュートリアルを役に立つならば、私が私のYouTubeチャンネルに加入することを考えて、私は記録します
定期的に、またはTwitter上で私に従ってプログラミングのコンテンツ.
社会
Github
Patreon
Reference
この問題について(Nextjsと概念APIによって供給されるブログを構築してください), 我々は、より多くの情報をここで見つけました https://dev.to/solomon04/build-a-blog-powered-by-nextjs-notion-api-57pjテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol