Server Components
Server components are React components that run on the server, with access to the server environment, making them ideal for data fetching.
// app/page.js
import { groq } from "next-sanity";
import client from "@/lib/sanity.client";
export default async function Page() {
const query = groq`*[_type == "post"] {
title,
author->name,
_id
}`;
const posts = await client.fetch(query);
return (
<div>
<h1>Posts</h1>
<ul>
{posts.map((post) => (
<li key={post._id}>
<h2>{post.title}</h2>
<p>By {post.author.name}</p>
</li>
))}
</ul>
</div>
);
}Client Components
Client components are React components that run on the client, suitable for handling user interactions and state management.
// app/page.js
import { useState, useEffect } from "react";
export default function Page() {
const [data, setData] = useState(null);
useEffect(() => {
async function fetchData() {
const res = await fetch("/api/data");
if (!res.ok) {
throw new Error("Failed to fetch data");
}
const data = await res.json();
setData(data);
}
fetchData().catch(console.error);
}, []);
return (
<div>
<h1>Data</h1>
{data ? (
<pre>{JSON.stringify(data, null, 2)}</pre>
) : (
<p>Loading...</p>
)}
</div>
);
}Dynamic Routes
Dynamic routes allow fetching data based on parameters using generateStaticParams and generateDynamicParams.
// app/posts/[id]/page.js
import { groq } from "next-sanity";
import client from "@/lib/sanity.client";
export async function generateStaticParams() {
const query = groq`*[_type == "post"]{_id}`;
const posts = await client.fetch(query);
return posts.map((post) => ({ id: post._id }));
}
export default async function PostPage({ params }) {
const query = groq`*[_type == "post" && _id == $id][0] {
title,
content,
author->name,
_id
}`;
const post = await client.fetch(query, { id: params.id });
return (
<div>
<h1>{post.title}</h1>
<p>By {post.author.name}</p>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</div>
);
}Server Actions
Server actions enable asynchronous operations on the server, such as data fetching or submission.
// app/page.js
import { groq } from "next-sanity";
import client from "@/lib/sanity.client";
export default async function Page() {
const posts = await getPosts();
return (
<div>
<h1>Posts</h1>
<ul>
{posts.map((post) => (
<li key={post._id}>
<h2>{post.title}</h2>
<p>By {post.author.name}</p>
</li>
))}
</ul>
</div>
);
}
async function getPosts() {
const query = groq`*[_type == "post"] {
title,
author->name,
_id
}`;
const posts = await client.fetch(query);
return posts;
}Server Rendering
Server rendering generates HTML content on the server and sends it to the client, which is the default behavior in Next.js.
// app/page.js
export default function Page() {
return (
<div>
<h1>Welcome to My App</h1>
<p>This is a server-rendered page.</p>
</div>
);
}Client Rendering
Client rendering generates and updates the DOM on the client, typically used for interactive components.
// app/page.js
import { useState } from "react";
export default function Page() {
const [count, setCount] = useState(0);
return (
<div>
<h1>Welcome to My App</h1>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}Incremental Static Generation (ISR)
Incremental Static Generation allows pre-generating static pages and updating them on-demand after deployment.
// app/posts/[id]/page.js
import { groq } from "next-sanity";
import client from "@/lib/sanity.client";
export async function generateStaticParams() {
const query = groq`*[_type == "post"]{_id}`;
const posts = await client.fetch(query);
return posts.map((post) => ({ id: post._id }));
}
export async function getStaticProps({ params }) {
const query = groq`*[_type == "post" && _id == $id][0] {
title,
content,
author->name,
_id
}`;
const post = await client.fetch(query, { id: params.id });
return {
props: {
post,
},
revalidate: 60, // Revalidate every 60 seconds
};
}
export default function PostPage({ post }) {
return (
<div>
<h1>{post.title}</h1>
<p>By {post.author.name}</p>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</div>
);
}Lazy Loading
Lazy loading is an optimization technique that loads components only when needed, improving the initial load speed of the application.
// app/page.js
import { lazy, Suspense } from "react";
const LazyComponent = lazy(() => import("./LazyComponent"));
export default function Page() {
return (
<div>
<h1>Welcome to My App</h1>
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
</div>
);
}Concurrent Rendering
Concurrent rendering, introduced in React 18, allows React to render components in parallel on background threads, improving rendering performance.
// app/page.js
import { useEffect, useState } from "react";
export default function Page() {
const [data, setData] = useState(null);
useEffect(() => {
async function fetchData() {
const res = await fetch("/api/data");
if (!res.ok) {
throw new Error("Failed to fetch data");
}
const data = await res.json();
setData(data);
}
fetchData().catch(console.error);
}, []);
return (
<div>
<h1>Data</h1>
{data ? (
<pre>{JSON.stringify(data, null, 2)}</pre>
) : (
<p>Loading...</p>
)}
</div>
);
}Error Boundaries
Error boundaries are a React mechanism for catching and handling JavaScript errors in the component tree.
// app/ErrorBoundary.js
import { ErrorBoundary as ReactErrorBoundary } from "react-error-boundary";
export default function ErrorBoundary({ children, FallbackComponent }) {
return (
<ReactErrorBoundary FallbackComponent={FallbackComponent}>
{children}
</ReactErrorBoundary>
);
}
// app/FallbackComponent.js
export default function FallbackComponent({ error, resetErrorBoundary }) {
return (
<div role="alert">
<p>Something went wrong:</p>
<pre>{error.message}</pre>
<button onClick={resetErrorBoundary}>Try again</button>
</div>
);
}
// app/page.js
import ErrorBoundary from "./ErrorBoundary";
import FallbackComponent from "./FallbackComponent";
export default function Page() {
return (
<ErrorBoundary FallbackComponent={FallbackComponent}>
<div>
<h1>Welcome to My App</h1>
<p>This is a server-rendered page.</p>
</div>
</ErrorBoundary>
);
}Redirects
Redirects allow redirecting users from one URL to another.
// app/api/redirect/route.js
export async function GET(request) {
return new Response(null, {
status: 301,
headers: {
location: "/new-url",
},
});
}
// app/page.js
import Link from "next/link";
export default function Page() {
return (
<div>
<h1>Welcome to My App</h1>
<p>This is a server-rendered page.</p>
<Link href="/api/redirect">Redirect Me</Link>
</div>
);
}Streaming Rendering
Streaming rendering enables progressive rendering of components, rather than waiting for all content to be ready before rendering.
// app/page.js
import { useEffect, useState } from "react";
export default function Page() {
const [data, setData] = useState(null);
useEffect(() => {
async function fetchData() {
const res = await fetch("/api/data");
if (!res.ok) {
throw new Error("Failed to fetch data");
}
const data = await res.json();
setData(data);
}
fetchData().catch(console.error);
}, []);
return (
<div>
<h1>Data</h1>
{data ? (
<pre>{JSON.stringify(data, null, 2)}</pre>
) : (
<p>Loading...</p>
)}
</div>
);
}Preloading
Preloading is an optimization technique that loads resources in advance for quick access when needed.
// app/page.js
import Link from "next/link";
export default function Page() {
return (
<div>
<h1>Welcome to My App</h1>
<p>This is a server-rendered page.</p>
<Link href="/about" prefetch>
About Us
</Link>
</div>
);
}Server Push
Server push is an optimization technique that allows the server to proactively send resources to the client without explicit requests.
// app/page.js
import { useEffect, useState } from "react";
export default function Page() {
const [data, setData] = useState(null);
useEffect(() => {
async function fetchData() {
const res = await fetch("/api/data");
if (!res.ok) {
throw new Error("Failed to fetch data");
}
const data = await res.json();
setData(data);
}
fetchData().catch(console.error);
}, []);
return (
<div>
<h1>Data</h1>
{data ? (
<pre>{JSON.stringify(data, null, 2)}</pre>
) : (
<p>Loading...</p>
)}
</div>
);
}Server-Side Rendering (SSR)
Server-side rendering generates HTML content on the server and sends it to the client.
// app/page.js
export default function Page() {
return (
<div>
<h1>Welcome to My App</h1>
<p>This is a server-rendered page.</p>
</div>
);
}



