使用 Next.js 和 Sanity 构建现代博客:分步指南

WBOY
发布: 2024-08-19 17:14:03
原创
670 人浏览过

Ein Blog ohne CMS kann zu endloser Frustration und Zeitverschwendung führen. Sanity.io vereinfacht den gesamten Prozess, sodass Sie sich auf Ihre Inhalte konzentrieren können.

Im heutigen Artikel erfahren Sie, wie Sie mit Sanity CMS und Next.js 14 einen Blog erstellen.

Am Ende dieses Leitfadens verfügen Sie über einen voll funktionsfähigen und leicht zu verwaltenden Blog.

Voraussetzung

Um diesem Artikel folgen zu können, müssen Sie über die folgenden Fähigkeiten verfügen:

  1. Kenntnisse in HTML, CSS und JavaScript
  2. Grundlagen von Next.js und TypeScript
  3. Grundlegendes Verständnis von Tailwind CSS
  4. Und Node.js auf Ihrem Computer installiert.

Was ist geistige Gesundheit?

Sanity ist ein Headless-CMS, das das Content-Management unkompliziert und effizient macht. Mit dem intuitiven Dashboard von Sanity Studio können Sie Ihre Inhalte ganz einfach nach Ihren Wünschen erstellen, bearbeiten und organisieren.

Sanity bietet außerdem eine flexible API und Webhook-Unterstützung, sodass Sie die volle Kontrolle darüber haben, wie und wo Ihre Inhalte bereitgestellt werden. Ganz gleich, ob es sich um eine Website, eine mobile App oder eine andere Plattform handelt, Sanity stellt sicher, dass Ihre Inhalte immer aktuell und zugänglich sind.

Initialisieren Sie das Next.js-Projekt

Wir integrieren Sanity in ein Next.js-Projekt. Daher müssen wir zuerst ein next.js-Projekt einrichten.

Um ein next.js-Projekt zu erstellen, führen Sie den folgenden Befehl aus:

npx create-next-app@latest
登录后复制

Folgen Sie den Anweisungen am Terminal und wählen Sie den Namen aus. Anschließend können Sie mit dem Standardvorschlag fortfahren.

Build a Modern Blog with Next.js & Sanity: A Step-by-Step Guide

Dadurch wird ein einfaches Next.js-Projekt generiert.

Jetzt öffnen wir das Projekt in unserem Code-Editor.

cd sanity-blog code .
登录后复制

Führen Sie nun den Befehl dev aus, um das Projekt auf Localhost:3000
zu öffnen.

npm run dev
登录后复制

Richten Sie Sanity Studio ein

Sanity Studio ist das Dashboard, in dem Sie Ihre Inhalte verwalten.

Sie können das Studio erstellen und unabhängig bereitstellen. Aber wir werden das Studio in unser Next.js-Projekt einbetten. Es ist einfach zu warten und zu verwenden.

Also erstellen wir ein Sanity-Projekt und integrieren es dann in unser Next.js-Projekt.

Führen Sie diesen Befehl aus, um ein Sanity-Projekt zu initialisieren.

npm create sanity@latest
登录后复制

Wenn Sie diesen Befehl ausführen, werden Sie aufgefordert, sich bei Sanity anzumelden. Wenn Sie bereits ein Konto haben, wählen Sie einen Anbieter und melden Sie sich bei Ihrem Konto an.
Wenn Sie noch kein Konto haben, erstellen Sie ein Konto und führen Sie den Installationsbefehl noch einmal aus.

Sobald Sie diesen Befehl ausführen, werden Ihnen eine Reihe von Fragen zur Konfiguration Ihres Projekts gestellt.

Sie können mit den Standardoptionen fortfahren.

Wir brauchen nur den Namen des Projekts und der Rest ist nicht so wichtig.

Looks like you already have a Sanity-account. Sweet! ✔ Fetching existing projects ? Select project to use Create new project ? Your project name: sanity-blog Your content will be stored in a dataset that can be public or private, depending on whether you want to query your content with or without authentication. The default dataset configuration has a public dataset named "production". ? Use the default dataset configuration? Yes ✔ Creating dataset ? Project output path: /home/amrin/Desktop/writing/sanity-blog ? Select project template Clean project with no predefined schema types ? Do you want to use TypeScript? Yes ✔ Bootstrapping files from template ✔ Resolving latest module versions ✔ Creating default project files ? Package manager to use for installing dependencies? npm Running 'npm install --legacy-peer-deps'
登录后复制

Installieren Sie die Abhängigkeiten

Bevor wir Sanity Studio in unseren Next.js-Blog integrieren, müssen wir diese Abhängigkeiten installieren.

npm install sanity next-sanity --save
登录后复制

Integrieren Sie Sanity in das Next.js-Projekt

Um Sanity in Next.js zu integrieren, benötigen wir den Projektnamen und die Projekt-ID. Das können wir über das Sanity-Dashboard abrufen.

Gehen Sie zu sanity.io/manage, dort sehen Sie alle Projekte.

Klicken Sie auf den Projekttitel, um die Details anzuzeigen.

Build a Modern Blog with Next.js & Sanity: A Step-by-Step Guide

Sie werden etwa Folgendes sehen:

Build a Modern Blog with Next.js & Sanity: A Step-by-Step Guide

Fahren Sie fort und kopieren Sie den Projektnamen und die Projekt-ID und fügen Sie diese Ihrer .env-Datei hinzu

NEXT_PUBLIC_SANITY_PROJECT_TITLE = ""; NEXT_PUBLIC_SANITY_PROJECT_ID = "";
登录后复制

Erstellen Sie nun die Konfigurationsdatei im Stammverzeichnis Ihres Projektordners. Und nennen Sie essanity.config.ts

import { defineConfig } from "sanity"; import {structureTool} from "sanity/structure"; import schemas from "@/sanity/schemas"; const config = defineConfig({ projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID as string, title: process.env.NEXT_PUBLIC_SANITY_PROJECT_TITLE as string, dataset: "production", apiVersion: "2023-06-18", basePath: "/admin", plugins: [structureTool()], schema: { types: schemas }, }); export default config;
登录后复制

Hier ist ein kurzer Überblick über den Inhalt der Konfigurationsdatei:

Importieren Sie zunächst die benötigten Funktionen und Dateien. Definieren Sie dann die Konfiguration. Die Konfiguration enthält viele Optionen:

projectId:Es ist die Sanity-Projekt-ID, die Sie zuvor erstellt haben.

title:Ihr Sanity-Projekttitel.

dataset:Definieren des Datensatzes für das Studio.

basePath:Der Pfad für das Studio. Wir verwenden die Route /admin, um auf das Studio zuzugreifen. Sie können jede gewünschte Route wählen.

schema:Dies sind die Schemata für den Inhalt. Ein Schema definiert, wie ein Dokument aussehen wird und welche Felder es enthalten wird. Wir werden ein Schema für Beiträge und Autoren, Kategorien und andere haben.

Wir haben das Schema noch nicht, wir werden es in einer Weile erstellen.

Richten Sie das Studio ein

Um das Studio einzurichten, benötigen wir zunächst eine Route. Navigieren Sie zusrc/app, erstellen Sie dann eine Routengruppe und nennen Sie siestudio.Wir gruppieren dies, um die Site von der Studioroute zu trennen.

Inside the studio create anadminfolder and inside that add a catch-all route.

└── (studio) ├── admin └── [[...index]] └── page.tsx
登录后复制

Include this code in the admin route. We are getting thesanity.configwe created earlier and NextStudio from sanity Studio to initialize the Studio.

"use client"; import config from "../../../../../sanity.config"; import { NextStudio } from "next-sanity/studio"; export default function AdminPage() { return ; }
登录后复制

We are almost done setting up the studio.
Lastly, we need to write the schemas for the content. After that, we can take a look into the studio.

Create The Schema

A Schema defines the structure of a document in the Studio. We define schema with properties.

Some of the properties are required and some are not.

The common properties are:

name:Name of the Schema. We will use this name to fetch the data.

title:Human readable title for the Schema. It will be visible in the Studio.

type: A valid document type.

fields:An array of all the properties of the document. If it’s a post schema the fields will have properties like Title, slug, body, meta description, etc. These properties will show up as input fields on the Studio.

Since we are building a blog we will have multiple Schemas such as:

  • Post
  • Author
  • Category

To learn more about Sanity Schema Visit the documentation.

Post Schema

Create a folder named sanity inside thesrcdirectory.

Inside that create another folder namedschemasandcreateindex.ts and post.ts file

Here’s what the post Schema looks like.

const post = { name: "post", title: "Post", type: "document", fields: [ { name: "title", title: "Title", type: "string", validation: (Rule: any) => Rule.required(), }, { name: "metadata", title: "Metadata", type: "string", validation: (Rule: any) => Rule.required(), }, { name: "slug", title: "Slug", type: "slug", options: { source: "title", unique: true, slugify: (input: any) => { return input .toLowerCase() .replace(/\s+/g, "-") .replace(/[^\w-]+/g, ""); }, }, validation: (Rule: any) => Rule.required().custom((fields: any) => { if ( fields?.current !== fields?.current?.toLowerCase() || fields?.current.split(" ").includes("") ) { return "Slug must be lowercase and not be included space"; } return true; }), }, { name: "tags", title: "Tags", type: "array", validation: (Rule: any) => Rule.required(), of: [ { type: "string", validation: (Rule: any) => Rule.custom((fields: any) => { if ( fields !== fields.toLowerCase() || fields.split(" ").includes("") ) { return "Tags must be lowercase and not be included space"; } return true; }), }, ], }, { name: "author", title: "Author", type: "reference", to: { type: "author" }, validation: (Rule: any) => Rule.required(), }, { name: "mainImage", title: "Main image", type: "image", options: { hotspot: true, }, // validation: (Rule: any) => Rule.required(), }, { name: "publishedAt", title: "Published at", type: "datetime", validation: (Rule: any) => Rule.required(), }, { name: "body", title: "Body", type: "blockContent", validation: (Rule: any) => Rule.required(), }, ], preview: { select: { title: "title", author: "author.name", media: "mainImage", }, prepare(selection: any) { const { author } = selection; return Object.assign({}, selection, { subtitle: author && `by ${author}`, }); }, }, }; export default post;
登录后复制

Copy the schema over to the post.ts file.

To save time we are not going to see the other schemas, you can get them from the repository.

Load the schemas

Open up theindex.tsfile and add this code snippet.

import author from "./author"; import blockContent from "./blockContent"; import category from "./category"; import post from "./post"; const schemas = [post, author, category, blockContent]; export default schemas;
登录后复制

We are importing all the schema in this file and creating an array to load the schema on the studio.

Now you can add posts from the studio.

To create a new post, go to localhost:3000/admin you will see all the schemas there. Go ahead and create a few posts.

Build a Modern Blog with Next.js & Sanity: A Step-by-Step Guide

Query the content with GROQ

We integrated the Studio and created a few posts. Now we need a way to fetch those posts and render them on the home page.

We will use GROQ to do exactly that. GROQ is a query language designed to query large schema-less JSON data collection. With GROQ expressive filtering we can fetch data the way we want to use it. It can join and fetch from multiple documents.

To start using GROQ we need to create the config file and the queries.

Go ahead and create these files inside thesanityfolder.

└── sanity ├── config │ └── client-config.ts ├── sanity-query.ts ├── sanity-utils.ts
登录后复制

Paste this code into theclient-config.tsfile.

import { ClientPerspective } from "next-sanity"; const config = { projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID as string, dataset: "production", apiVersion: "2023-03-09", useCdn: false, token: process.env.SANITY_API_KEY as string, perspective: 'published' as ClientPerspective, }; export default config;
登录后复制

This is the config for fetching the data with the GROQ query.

Here’s a quick break-down of the config options:

apiVersion: It’s the Sanity API version. You can use the current date.

useCDN: Used to disable edge cache. We are setting it to false as we will integrate webhook. It will deliver updated data.

token:Sanity API key. Required for webhook integration. (We will integrate webhook in the next section)

perspective: To determine the document status. If it’s set topublishedit will only query the published documents otherwise it will fetch all the document drafts andpublished.

Now we will write the queries. We are going to keep the Queries and the Fetch functions in separate files.

Here are the queries, copy these into thesanity-query.tsfile.

import { groq } from "next-sanity"; const postData = `{ title, metadata, slug, tags, author->{ _id, name, slug, image, bio }, mainImage, publishedAt, body }`; export const postQuery = groq`*[_type == "post"] ${postData}`; export const postQueryBySlug = groq`*[_type == "post" && slug.current == $slug][0] ${postData}`; export const postQueryByTag = groq`*[_type == "post" && $slug in tags[]->slug.current] ${postData}`; export const postQueryByAuthor = groq`*[_type == "post" && author->slug.current == $slug] ${postData}`; export const postQueryByCategory = groq`*[_type == "post" && category->slug.current == $slug] ${postData}`;
登录后复制

At the top, is the postData object, which defines all the properties we want to fetch.

Then the actual queries. Each query has the query first then the postData object.

These queries are self-descriptive, but for clarity here’s a quick explanation for the postQueryBySlug:

_type:The name of the document.

slug.current:Checking the slug of each of the documents if it matches with $slug (We will pass $slug with the fetch function).

postData: Filtering out the data we want to get i.e. the title, mainImage, and description.

We will use these queries to fetch the data from Sanity Studio.

Copy these codes into thesanity-utils.tsfile.

import ImageUrlBuilder from "@sanity/image-url"; import { createClient, type QueryParams } from "next-sanity"; import clientConfig from "./config/client-config"; import { postQuery, postQueryBySlug } from "./sanity-query"; import { Blog } from "@/types/blog"; export const client = createClient(clientConfig); export function imageBuilder(source: string) { return ImageUrlBuilder(clientConfig).image(source); } export async function sanityFetch({ query, qParams, tags, }: { query: string, qParams: QueryParams, tags: string[], }): Promise { return ( client.fetch < QueryResponse > (query, qParams, { cache: "force-cache", next: { tags }, }) ); } export const getPosts = async () => { const data: Blog[] = await sanityFetch({ query: postQuery, qParams: {}, tags: ["post", "author", "category"], }); return data; }; export const getPostBySlug = async (slug: string) => { const data: Blog = await sanityFetch({ query: postQueryBySlug, qParams: { slug }, tags: ["post", "author", "category"], }); return data; };
登录后复制

Here’s a quick overview of what’s going on here:

client: creating a Sanity client with the config we created earlier. It will be used to fetch the data with GROQ.

imageBuilder: To use the post image. The images are provided from sanity cdn and it requires all these configs.

sanityFetch: It’s the function to fetch data with cache. ( We could just use fetch too but we are configuring this now so that we can just add the webhook and we are good to go. )

Create the type for the post

Since we are using typescript we need to write the Type for the post. You can see we are using Blog type on the query functions.

Create ablog.tsfile inside the types folder and copy this type:

import { PortableTextBlock } from "sanity"; export type Author = { name: string, image: string, bio?: string, slug: { current: string, }, _id?: number | string, _ref?: number | string, }; export type Blog = { _id: number, title: string, slug: any, metadata: string, body: PortableTextBlock[], mainImage: any, author: Author, tags: string[], publishedAt: string, };
登录后复制

All the types are normal, the PortableTextBlock is from sanity. It defines the type of the post body.

All the setup is done!

Let’s fetch the posts and render them on our Next.js project.

Render the content on the Next.js project

First, we will fetch all the posts and create the blog page. Then we will fetch the post by slug for the single post page.

Post Archive

Create theBlogcomponentapp/components/Blog/index.tsand add this code.

import { Blog } from "@/types/blog"; import Link from "next/link"; import React from "react"; const BlogItem = ({ blog }: { blog: Blog }) => { return (  

{blog.title}

{new Date(blog.publishedAt).toDateString()}

{blog.metadata.slice(0, 140)}...

); }; export default BlogItem;
登录后复制

Remove all the styles and code fromglobals.css(keep the tailwind utils) file andpage.tsxfile

Now add this to thepage.tsxfile inside (site)

import { getPosts } from "@/sanity/sanity-utils"; import BlogItem from "@/components/Blog"; export default async function Home() { const posts = await getPosts(); return ( 
{posts?.length > 0 ? ( posts.map((post: any) => ) ) : (

No posts found

)}
); }
登录后复制

First import thegetPostsfunction andBlogItem.Inside the Home component fetch the posts and render them.

We need a navbar to navigate between pages.

To save time I already created the Header file and loaded it inside the layout.tsx file.

Check out the source code and copy the Header component from there.

import Header from "@/components/Header"; export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode, }>) { return ( <> 
{children}
); }
登录后复制

This is how it looks:

Build a Modern Blog with Next.js & Sanity: A Step-by-Step Guide

Single post

Now we need to create a single post page so that we can read the post.

Create the single post page insideblog/[slug]/page.tsxand paste this code snippet.

import React from "react"; import { getPostBySlug } from "@/sanity/sanity-utils"; import RenderBodyContent from "@/components/Blog/RenderBodyContent"; const SingleBlogPage = async ({ params }: { params: any }) => { const post = await getPostBySlug(params.slug); return ( 

{post.title}

Published: {new Date(post.publishedAt).toDateString()} by {post.author.name}

{post.metadata}

); }; export default SingleBlogPage;
登录后复制

First importgetPostBySlugandRenderBodyContent(we will create it in a while).

Fetch the post by slug and render the post withRenderBodyContent.

Render body content

It’s a custom component to render the post body.
CreateRenderBodyContent.tsxfile inside thecomponents/Blogfolder*.*

import config from "@/sanity/config/client-config"; import { Blog } from "@/types/blog"; import { PortableText } from "@portabletext/react"; import { getImageDimensions } from "@sanity/asset-utils"; import urlBuilder from "@sanity/image-url"; import Image from "next/image"; // lazy-loaded image component const ImageComponent = ({ value, isInline }: any) => { const { width, height } = getImageDimensions(value); return ( 
{value.alt
); }; const components = { types: { image: ImageComponent, }, }; const RenderBodyContent = ({ post }: { post: Blog }) => { return ( <> ); }; export default RenderBodyContent;
登录后复制

This component will handle special types differently. We are only handling Images here.

You can include code blocks, embeds, and many more. You can find more information on Sanity plugins on Sanity.

Here’s what it looks like.

Build a Modern Blog with Next.js & Sanity: A Step-by-Step Guide

To make the post look like this install the tailwind/typography plugin and load that inside thetailwind.config.tsfile.

npm install @tailwindcss/typography
登录后复制

Webhook Integration

We will integrate Sanity webhook to fetch the data on time. Otherwise, you will have to deploy the site every time you write a post.

We already added revalidation on the fetch functions. Right now we need to generate the Keys and create the Webhook endpoint.

Generate the API key

Go to sanity.io/manage and navigate toAPI→Tokensthen click on theAdd API tokenbutton.

Build a Modern Blog with Next.js & Sanity: A Step-by-Step Guide

Give your API a name then choose Editor and save.

Build a Modern Blog with Next.js & Sanity: A Step-by-Step Guide

You will get an API key and save it on the env file

SANITY_API_KEY = "YOUR_API_KEY";
登录后复制

Create the Webhook endpoint

First, create the webhook endpointapp/api/revalidate/route.tsand add this code snippet.

import { revalidateTag } from "next/cache"; import { type NextRequest, NextResponse } from "next/server"; import { parseBody } from "next-sanity/webhook"; export async function POST(req: NextRequest) { try { const { body, isValidSignature } = await parseBody<{ _type: string; slug?: string | undefined; }>(req, process.env.NEXT_PUBLIC_SANITY_HOOK_SECRET); if (!isValidSignature) { return new Response("Invalid Signature", { status: 401 }); } if (!body?._type) { return new Response("Bad Request", { status: 400 }); } revalidateTag(body._type); return NextResponse.json({ status: 200, revalidated: true, now: Date.now(), body, }); } catch (error: any) { console.error(error); return new Response(error.message, { status: 500 }); } }
登录后复制

We are using tag-based revalidation in this webhook.

This endpoint will be called by the webhook every time you create, delete, or update a document from Sanity Studio.

Generate the Webhook Secret

Navigate to sanity.io/manageAPI→Webhooks.Click on theCreate Webhookbutton.

Build a Modern Blog with Next.js & Sanity: A Step-by-Step Guide

You will get a modal with a form. Fill in the form with the following pieces of information:

Name:Name of the Webhook

Description:Short description of what the webhook does (This is an optional field).

URL:Set the URL to https://YOUR_SITE_URL/api/revalidate

Dataset:Choose your desired dataset or leave the default value.

Trigger on:Set the hook to trigger on"Create","Update", and"Delete".

Filter:Leave this field blank.

Projections:Set the projections to{_type, "slug": slug.current}
Status:
Check the enable webhook box.

HTTP Method:POST.

Leave HTTP headers, API version, and Draft as default.

Secret:Give your webhook a unique secret and copy it.

Hit save to create your webhook.

Save your webhook in the .env file under this variable name.

SANITY_HOOK_SECRET=YOUR_SECRET
登录后复制

Testing the webhook:Go ahead and change the content of an Article and publish it. After that hard reload your website you should see the changes in real time.

Note: You can test webhook from the live site or you can choose tools like ngrok to expose the localhost URL and use that to test it.

Conclusion

That’s it you built a blog with Sanity CMS. Congrats! ?

Even though this guide looks so long, it’s just the beginning. Sanity has more features and options, you can build cool things.
It’s impossible to cover everything in a single article.

I will suggest you to checkout these resources to learn more and improve your blog

  • Sanity docs
  • Code highlighter
  • Sanity Plugins

  • Source Code

Connect With Me

I hope you enjoyed the post, if you want to stay conntected with me checkout my socials.
Would love to talk to you!

Twitter/x

Github

LinkedIn

Happy Coding.

以上是使用 Next.js 和 Sanity 构建现代博客:分步指南的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责声明 Sitemap
PHP中文网:公益在线PHP培训,帮助PHP学习者快速成长!