Next.js provides multiple data fetching methods, and developers can choose the appropriate approach based on different rendering strategies and requirements. Here are the main data fetching methods in Next.js:
Pages Router Data Fetching Methods
1. getStaticProps
Fetch data at build time for static site generation (SSG).
javascriptexport async function getStaticProps(context) { const data = await fetch('https://api.example.com/data').then(r => r.json()); return { props: { data }, revalidate: 60, // Optional: ISR, regenerate every 60 seconds notFound: false, // Optional: Return 404 page redirect: { destination: '/login', permanent: false }, // Optional: Redirect }; } export default function Page({ data }) { return <div>{data.content}</div>; }
Use cases:
- Data is available at build time
- Page content doesn't change frequently
- Need pre-rendering for better SEO
2. getServerSideProps
Fetch data on each request for server-side rendering (SSR).
javascriptexport async function getServerSideProps(context) { const { req, res, query, params } = context; // Can access request and response objects const token = req.cookies.token; const data = await fetch('https://api.example.com/data', { headers: { Authorization: `Bearer ${token}` } }).then(r => r.json()); return { props: { data }, // Doesn't support revalidate }; } export default function Page({ data }) { return <div>{data.content}</div>; }
Use cases:
- Data is only available at request time
- Need to access request/response objects
- Content changes frequently
3. getStaticPaths
Used for static generation of dynamic routes, defining all possible paths.
javascriptexport async function getStaticPaths() { const posts = await getAllPosts(); return { paths: posts.map(post => ({ params: { slug: post.slug } })), fallback: false, // Or 'blocking' or true }; } export async function getStaticProps({ params }) { const post = await getPostBySlug(params.slug); return { props: { post }, }; } export default function BlogPost({ post }) { return <div>{post.title}</div>; }
fallback options:
false: Only return pre-generated paths, other paths return 404'blocking': Server renders new paths, waits for completion before returningtrue: Immediately return static page, generate new paths in background
App Router Data Fetching Methods
1. fetch in Server Components
Use fetch directly in server components to get data.
javascriptasync function Page() { const data = await fetch('https://api.example.com/data', { cache: 'force-cache', // Or 'no-store', 'no-cache', 'default' next: { revalidate: 60, // ISR tags: ['data'] // For on-demand revalidation } }).then(r => r.json()); return <div>{data.content}</div>; }
cache options:
force-cache: Force use of cache (default)no-store: Don't use cacheno-cache: Validate cache every timedefault: Use default cache strategy
2. Using React Server Components
javascriptasync function BlogList() { const posts = await fetch('https://api.example.com/posts', { next: { revalidate: 3600 } }).then(r => r.json()); return ( <div> {posts.map(post => ( <PostCard key={post.id} post={post} /> ))} </div> ); }
3. Using Suspense and Streaming
javascriptimport { Suspense } from 'react'; async function SlowComponent() { const data = await fetch('https://api.example.com/slow', { next: { revalidate: 60 } }).then(r => r.json()); return <div>{data.content}</div>; } export default function Page() { return ( <div> <h1>Page Title</h1> <Suspense fallback={<div>Loading...</div>}> <SlowComponent /> </Suspense> </div> ); }
Client-Side Data Fetching
1. Using useEffect
javascript'use client'; import { useState, useEffect } from 'react'; export default function ClientDataComponent() { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { fetch('/api/data') .then(res => res.json()) .then(data => { setData(data); setLoading(false); }); }, []); if (loading) return <div>Loading...</div>; return <div>{data.content}</div>; }
2. Using SWR
javascript'use client'; import useSWR from 'swr'; const fetcher = (url) => fetch(url).then(res => res.json()); export default function SWRComponent() { const { data, error, isLoading } = useSWR('/api/data', fetcher, { revalidateOnFocus: false, revalidateOnReconnect: false, dedupingInterval: 60000, }); if (isLoading) return <div>Loading...</div>; if (error) return <div>Error</div>; return <div>{data.content}</div>; }
3. Using React Query
javascript'use client'; import { useQuery } from '@tanstack/react-query'; async function fetchData() { const res = await fetch('/api/data'); return res.json(); } export default function ReactQueryComponent() { const { data, error, isLoading } = useQuery({ queryKey: ['data'], queryFn: fetchData, staleTime: 60000, cacheTime: 300000, }); if (isLoading) return <div>Loading...</div>; if (error) return <div>Error</div>; return <div>{data.content}</div>; }
Data Fetching Best Practices
1. Choose the Right Method
| Scenario | Recommended Method |
|---|---|
| Static content, available at build time | getStaticProps / SSG |
| Dynamic content, needs real-time data | getServerSideProps / SSR |
| Needs user interaction | Client-side data fetching |
| SEO important, content changes infrequently | SSG + ISR |
| Need to access request/response objects | getServerSideProps |
2. Caching Strategies
javascript// Long-term cache fetch('/api/data', { cache: 'force-cache', next: { revalidate: 3600 } }); // Short-term cache fetch('/api/data', { cache: 'no-store' }); // On-demand revalidation fetch('/api/data', { next: { tags: ['data'] } }); // Revalidate on demand in API routes import { revalidateTag } from 'next/cache'; export async function POST() { revalidateTag('data'); return Response.json({ revalidated: true }); }
3. Error Handling
javascriptexport async function getStaticProps() { try { const data = await fetchData(); return { props: { data } }; } catch (error) { return { notFound: true, }; } }
4. Loading States
javascript// App Router - Use loading.js // app/loading.js export default function Loading() { return <div>Loading...</div>; } // Pages Router - Use custom loading component export default function LoadingPage() { return <div>Loading...</div>; }
5. Parallel Data Fetching
javascript// Fetch multiple data sources in parallel export async function getStaticProps() { const [posts, users, comments] = await Promise.all([ fetch('/api/posts').then(r => r.json()), fetch('/api/users').then(r => r.json()), fetch('/api/comments').then(r => r.json()), ]); return { props: { posts, users, comments }, }; }
Performance Optimization Recommendations
- Use ISR: For content that needs regular updates, use ISR instead of SSR
- Cache data: Set reasonable cache times to reduce unnecessary requests
- Parallel fetching: Use Promise.all to fetch multiple data sources in parallel
- Streaming rendering: Use Suspense for streaming rendering to improve user experience
- Client-side caching: Use SWR or React Query to cache client-side data
- On-demand revalidation: Use tag system to revalidate data on demand
By properly choosing and using these data fetching methods, you can build high-performance Next.js applications with excellent user experience.