Choosing the Right Data Fetching Method
Choosing the Right Data Fetching Method
Section titled “Choosing the Right Data Fetching Method”Introduction
Section titled “Introduction”Next.js offers multiple data fetching methods: getStaticProps (SSG/ISR), getServerSideProps (SSR), and client-side data fetching. Choosing the right method is crucial for balancing performance, SEO, freshness, and development complexity. This guide helps you decide which approach to use based on your specific data and requirements.
Why do we need this?
Section titled “Why do we need this?”Using the wrong data fetching method can lead to:
- Poor performance and increased costs
- Stale or insecure data
- Negative SEO impact
- Poor user experience
- Unnecessary complexity Understanding the trade-offs between methods enables informed decisions that optimize your application for its specific use case.
Problem Statement
Section titled “Problem Statement”Developers often default to familiar methods without considering alternatives, leading to suboptimal applications. For example, using SSR for static content wastes resources, while using CSR for SEO-critical content hurts search rankings. We need a decision framework to select the appropriate data fetching method for each page and component.
Real World Story
Section titled “Real World Story”Imagine you’re building an enterprise SaaS application with multiple page types:
- Marketing homepage: Static content that rarely changes
- Product catalog: Semi-static content with periodic updates
- User dashboard: Personalized, frequently changing data
- Settings page: User-specific data that changes on interaction
- API endpoints: Server-only data processing By matching each page type to the appropriate data fetching method (SSG for homepage, ISR for catalog, SSR for dashboard, CSR for settings interactions), you optimize for performance, cost, and user experience across the entire application.
Real World Analogy
Section titled “Real World Analogy”Think of choosing a data fetching method like selecting transportation for a trip:
- Walking (SSG): Best for short distances with fixed scenery (static content)
- Bicycle (ISR): Good for medium distances with occasional scenery changes (periodically updated content)
- Car (SSR): Necessary for long distances or when you need to make stops along the way (frequently changing/personalized data)
- Teleportation (CSR): Instant for the traveler but requires setup time and doesn’t show the journey (client-side rendering)
Visual Explanation
Section titled “Visual Explanation”Data Freshness vs Performance Trade-off:----------------------------------------Highest Performance → [SSG] → [ISR] → [SSR] → [CSR] → Lowest Performance ←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←Lowest Freshness → [SSG] → [ISR] → [SSR] → [CSR] → Highest FreshnessTechnical Explanation
Section titled “Technical Explanation”Decision Factors
Section titled “Decision Factors”When choosing a data fetching method, consider these key factors:
-
Data Freshness Requirements:
- How frequently does the data change?
- Is stale acceptable for a period of time?
- Do users need to see updates immediately?
-
Performance Requirements:
- What are your latency requirements?
- What is your budget for server/compute costs?
- How important is instant loading?
-
SEO Requirements:
- Does the page need to rank well in search engines?
- Must content be immediately available to crawlers?
- Are there social sharing requirements?
-
Data Sensitivity:
- Is the data user-specific or personalized?
- Does the data contain sensitive information?
- Are there authorization requirements?
-
Technical Constraints:
- Are there build-time limitations (long builds, large datasets)?
- Are there runtime limitations (serverless function timeouts)?
- Are there dependencies on browser-only APIs?
Decision Matrix
Section titled “Decision Matrix”Use this table to guide your selection:
| Data Characteristic | Best Method | Alternatives Considered |
|---|---|---|
| Never changes | SSG | ISR |
| Changes infrequently (hourly/daily) | ISR | SSG (with rebuilds) |
| Changes frequently | SSR | ISR (low revalidate) |
| User-specific/personalized | SSR | CSR + API |
| Public but frequently changing | SSR | ISR + client updates |
| Depends on request-time info | SSR | - |
| Static but large dataset | SSG + incremental build | ISR |
| Needs immediate updates | SSR | CSR + WebSocket |
| SEO-critical | SSG/ISR/SSR | - (avoid pure CSR) |
| Interactive/dashboard | CSR + SSR hybrid | SSR |
Example: E-commerce Application Breakdown
Section titled “Example: E-commerce Application Breakdown”Let’s examine how different parts of an e-commerce site would choose their data fetching methods:
1. Homepage (SSG)
Section titled “1. Homepage (SSG)”export async function getStaticProps() { const { featuredProducts, categories, promotions } = await fetchHomepageData(); return { props: { featuredProducts, categories, promotions } };}// Why SSG: Content changes infrequently, needs to be fast and SEO-friendly2. Product Listing Page (ISR)
Section titled “2. Product Listing Page (ISR)”export async function getStaticProps({ params }) { const { category } = params; const products = await fetchProductsByCategory(category); return { props: { products, category }, revalidate: 300 // 5 minutes };}export async function getStaticPaths() { const categories = await fetchAllCategories(); return { paths: categories.map(c => ({ params: { category: c } })), fallback: 'blocking' };}// Why ISR: Products change periodically, need good SEO but can tolerate brief staleness3. Product Detail Page (SSR)
Section titled “3. Product Detail Page (SSR)”export async function getServerSideProps({ params }) { const { sku } = params; const product = await fetchProductBySKU(sku); const { reviews, relatedItems, inventory } = await fetchProductDetails(sku); if (!product) return { notFound: true }; return { props: { product, reviews, relatedItems, inventory } };}// Why SSR: Price and inventory change frequently, personalized recommendations needed4. Shopping Cart (CSR)
Section titled “4. Shopping Cart (CSR)”export default function Cart() { const [cartItems, setCartItems] = useState([]); const [loading, setLoading] = useState(true);
useEffect(() => { fetchCartItems().then(items => { setCartItems(items); setLoading(false); }); }, []);
// Why CSR: User-specific, changes frequently based on user actions, // not SEO-critical as it's behind authentication}5. User Profile (SSR)
Section titled “5. User Profile (SSR)”export async function getServerSideProps({ params }) { const { id } = params; // Check authentication and authorization const user = await getUserById(id); if (!user || user.id !== req.user.id) { return { redirect: { destination: '/', permanent: false } }; } return { props: { user } };}// Why SSR: User-specific data requiring authorization checks on each requestExample: News Application Breakdown
Section titled “Example: News Application Breakdown”1. Article Listing (SSG/ISR)
Section titled “1. Article Listing (SSG/ISR)”export async function getStaticProps({ params }) { const { category } = params; const articles = await fetchArticlesByCategory(category); return { props: { articles, category }, revalidate: 60 // 1 minute for breaking news };}// Why ISR: Needs frequent updates but still benefits from pre-rendering for SEO2. Article Detail (SSR)
Section titled “2. Article Detail (SSR)”export async function getServerSideProps({ params }) { const { id } = params; const article = await fetchArticleById(id); if (!article) return { notFound: true }; // Increment view count (requires server-side) await incrementViewCount(id); return { props: { article } };}// Why SSR: View counting requires server-side mutation, content must be fresh3. Comments Section (CSR)
Section titled “3. Comments Section (CSR)”export default function Comments({ articleId }) { const [comments, setComments] = useState([]);
useEffect(() => { const subscription = subscribeToComments(articleId); subscription.on('newComment', (comment) => { setComments(prev => [...prev, comment]); });
return () => subscription.unsubscribe(); }, [articleId]);
// Why CSR: Real-time updates, user-specific interactions, not SEO-critical}Technical Explanation: Hybrid Approaches
Section titled “Technical Explanation: Hybrid Approaches”Sometimes a single method isn’t sufficient. Consider these patterns:
1. SSR + Client Updates
Section titled “1. SSR + Client Updates”export default function StockChart({ initialData }) { const [data, setData] = useState(initialData);
useEffect(() => { const ws = new WebSocket(`wss://stocks.example.com/${symbol}`); ws.onmessage = (event) => { setData(JSON.parse(event.data)); }; return () => ws.close(); }, [symbol]);
return <StockChart data={data} />;}
export async function getServerSideProps({ params }) { const { symbol } = params; const initialData = await fetchInitialStockData(symbol); return { props: { initialData } };}// Why: SSR for initial load (SEO, fast first paint), CSR for real-time updates2. ISR + Client-Side Validation
Section titled “2. ISR + Client-Side Validation”export default function ProductDetails({ product }) { // Client-side validation of form inputs const [formErrors, setFormErrors] = useState({});
const handleSubmit = (formData) => { // Validate client-side first const errors = validateForm(formData); if (Object.keys(errors).length > 0) { setFormErrors(errors); return; }
// Then submit to server submitReview(product.sku, formData); };
return ( <div> <ProductInfo product={product} /> <ReviewForm onSubmit={handleSubmit} errors={formErrors} /> </div> );}
export async function getStaticProps({ params }) { const { sku } = params; const product = await fetchProductBySKU(sku); return { props: { product }, revalidate: 600 // 10 minutes };}// Why: ISR for product data (SEO, performance), CSR for interactive form validation3. SSG Skeleton + Client Data Fetch
Section titled “3. SSG Skeleton + Client Data Fetch”export default function Dashboard() { const [stats, setStats] = useState(null); const [loading, setLoading] = useState(true);
useEffect(() => { fetchDashboardStats().then(stats => { setStats(stats); setLoading(false); }); }, []);
if (loading) return <SkeletonLoader />; if (!stats) return <ErrorMessage />;
return <DashboardContent stats={stats} />;}
export async function getStaticProps() { // Return minimal props for shell - no data fetching return { props: { } };}// Why: SSG for instant shell/loading state, CSR for data fetching// Provides fast first paint while waiting for dataExample: Decision Flow Implementation
Section titled “Example: Decision Flow Implementation”Here’s a practical decision flow you could implement in your team:
START │ ├→ Is the page SEO-critical? (marketing, blog, product listings) │ │ │ ├→ Yes → Does data change frequently? │ │ │ │ │ ├→ No (changes < daily) → SSG │ │ │ │ │ └→ Yes (changes ≥ daily) → ISR │ │ │ └→ No → Continue to next check │ ├→ Does the page require authentication? │ │ │ ├→ Yes → Is the data user-specific/personalized? │ │ │ │ │ ├→ Yes → SSR │ │ │ │ │ └→ No → SSG/ISR (if applicable) │ │ │ └→ No → Continue to next check │ ├→ Does data depend on request-time info? (cookies, headers, IP) │ │ │ ├→ Yes → SSR │ │ │ └→ No → Continue to next check │ ├→ Is real-time updating required? (stock prices, chat, live scores) │ │ │ ├→ Yes → Hybrid: SSR/SSG/ISR for initial state + CSR/WebSocket for updates │ │ │ └→ No → Continue to next check │ ├→ Is the dataset extremely large (>100k items)? │ │ │ ├→ Yes → Consider pagination + SSG/ISR for pages │ │ │ └→ No → Evaluate based on freshness/performance needs │ └→ Default to SSG if unsure (safe, performant, SEO-friendly)Production Example
Section titled “Production Example”In production applications, teams often establish guidelines like:
E-commerce Platform Guidelines
Section titled “E-commerce Platform Guidelines”- Product Catalog Pages: ISR with 15-minute revalidate
- Reason: Balances SEO needs with inventory/price update frequency
- Flash Sale Pages: SSR with short-term caching (1-5 minutes)
- Reason: Prices change rapidly, but some caching still beneficial
- User Account Pages: SSR with authentication checks
- Reason: Personalized data requiring authorization on each request
- Blog Posts: SSG with daily rebuild trigger via webhook
- Reason: Content changes infrequently, needs perfect SEO
- Search Results: SSR with client-side faceted filtering
- Reason: Query-dependent results, but filtering can be client-side
- Shopping Cart: CSR with API sync
- Reason: Highly user-specific, not SEO-critical, frequent updates
- Checkout Process: SSR with payment service integration
- Reason: Security-sensitive, requires server-side validation
SaaS Application Guidelines
Section titled “SaaS Application Guidelines”- Marketing Site: SSG for all pages
- Reason: Static content, SEO critical
- Public Documentation: ISR with 1-hour revalidate
- Reason: Updates periodically, SEO important
- User Dashboard: SSR for initial load + CSR for widget updates
- Reason: Personalized data benefiting from SSR initial state
- Settings Pages: CSR with API persistence
- Reason: User-specific, interaction-based changes
- Analytics Reports: SSR with query parameters + client-side visualization
- Reason: Report parameters change per request, but rendering can be client-side
- Authentication Pages: SSR with CSR form handling
- Reason: Security-sensitive, benefits from SSR initial state
Folder Structure Context
Section titled “Folder Structure Context”Different data fetching methods coexist in the same application:
pages/├── index.js # SSG (marketing homepage)├── blog/│ ├── [slug].js # SSG/ISR (blog posts)│ └── index.js # SSG (blog listing)├── products/│ ├── [category].js # ISR (product listing by category)│ ├── [sku].js # SSR (product detail)│ └── index.js # SSG (product catalog)├── dashboard/│ ├── index.js # SSR + CSR (user dashboard)│ └── [widget].js # CSR (individual widgets)├── docs/│ ├── [slug].js # SSG (documentation)│ └── index.js # SSG (docs listing)├── cart.js # CSR (shopping cart)├── checkout/│ ├── index.js # SSR (checkout process)│ └── [step].js # CSR (checkout steps)├── search.js # SSR (search results)└── api/ └── ... # Server-only routesBest Practices
Section titled “Best Practices”- Establish Team Guidelines: Create decision matrices or flowcharts for your team
- Measure and Monitor: Track performance metrics (TTFB, FCP, etc.) for each method
- Consider Hybrid Solutions: Combine methods when one doesn’t fully satisfy requirements
- Plan for Evolution: Choose methods that can adapt to changing requirements
- Test Under Load: Validate performance characteristics under expected traffic
- Document Decisions: Record why each method was chosen for future reference
- Leverage Next.js Features:
- Use
revalidatefor ISR tuning - Use
fallbackoptions for dynamic routes - Consider Edge Runtime for geographic distribution
- Use
- Think About Caching:
- How will caching interact with your chosen method?
- Can you add caching layers to improve SSR/ISR efficiency?
- Consider Build Times:
- Will SSG/ISR lead to unreasonably long build times?
- Is incremental build or partial prerendering needed?
- ** Plan for Invalidations**:
- How will you purge caches when data changes?
- Do you need webhook-triggered rebuilds?
Common Mistakes
Section titled “Common Mistakes”- Defaulting to Familiar Methods: Always using SSR because it’s “safe”
- Ignoring SEO Requirements: Using CSR for content that needs indexing
- Overlooking User-Specific Data: Using SSG for personalized content
- Neglecting Performance Budgets: Choosing methods that exceed latency budgets
- Ignoring Data Volatility: Using SSG for rapidly changing data
- Forgetting About Authorization: Using SSG/ISR for protected content
- Over-Fetching in SSG/ISR: Making build process unnecessarily slow
- Underestimating Client Capabilities: Assuming users can’t handle CSR
- Ignoring Edge Cases: Not considering authentication failures, rate limits, etc.
- Failing to Re-evaluate: Not revisiting decisions as requirements change
Performance Notes
Section titled “Performance Notes”- Time to First Byte (TTFB):
- SSG/ISR: 10-50ms (CDN served static)
- SSR: 100-500ms+ (server processing)
- CSR: 50-100ms (minimal HTML) + data fetch latency
- First Contentful Paint (FCP):
- SSG/ISR: Nearly instant with TTFB
- SSR: After TTFB + HTML download
- CSR: After TTFB + JS download + execution
- Largest Contentful Paint (LCP):
- SSG/ISR: When main content renders in HTML
- SSR: When server-streamed content arrives
- CSR: After data fetch and client rendering
- First Input Delay (FID):
- SSG/ISR: Low (minimal JS hydration)
- SSR: Low (minimal JS hydration)
- CSR: Varies based on JS execution time
- Cumulative Layout Shift (CLS):
- All methods: Low when dimensions are known and content doesn’t shift
- Cost Comparison (monthly for 1M page views):
- SSG: $5-20 (static storage/CDN)
- ISR: $15-60 (occasional regeneration)
- SSR: $100-500+ (continuous compute)
- CSR: $5-20 (static) + API costs
- Scaling Characteristics:
- SSG/ISR: Scale horizontally at build time; serve statically
- SSD: Scale horizontally with request volume
- CSR: Scale based on API capacity; client handles rendering
Security Notes
Section titled “Security Notes”- Static Methods (SSG/ISR):
- Build-time only: Reduces attack surface at runtime
- No secrets: Only
NEXT_PUBLIC_*variables available during build - Immutable: Cannot be tampered with without rebuild
- CSP headers: Easily applied via static headers
- Server Method (SSR):
- Runtime exposure: Requires server security hardening
- Secrets access: Can use environment variables and secrets
- Input validation: Critical to prevent injection attacks
- Authentication: Natural place to check auth on each request
- Client Method (CSR):
- No secrets: Never store secrets in client-side code
- Input validation: Must validate/sanitize all data before display
- Authentication: Typically relies on tokens from secure cookies
- XSS protection: Critical to escape user-generated content
- Cross-Method Considerations:
- Data consistency: Ensure same data rules apply across methods
- Session handling: Consistent authentication across methods
- Audit logging: Consider where security events should be logged
SEO Considerations
Section titled “SEO Considerations”- Static Methods (SSG/ISR):
- Perfect for SEO: Instant, complete HTML available to crawlers
- Fast loading: Improves Core Web Vitals ranking factor
- Predictable: Same content for users and crawlers
- Structured data: Easy to implement correctly
- Server Method (SSR):
- Good for SEO: Complete HTML rendered for each request
- Loading speed: Depends on server response time
- Content consistency: Same for users and crawlers (assuming no auth differences)
- Dynamic content: Crawlers see fresh content on each crawl
- Client Method (CSR):
- Poor for SEO: Initial HTML lacks meaningful content
- JavaScript dependency: Content may not be indexed if JS fails
- Delayed availability: Content appears after JS execution
- Cloaking risk: Different content for users vs. crawlers if not careful
- Hybrid Approaches:
- Initial SSR/SSG/ISR: Provides SEO-friendly initial state
- Subsequent CSR updates: Enhances interactivity without losing SEO
- Careful implementation: Ensure crawlers see meaningful initial state
Interview Questions
Section titled “Interview Questions”- How do you decide between SSG, ISR, and SSR for a given page?
- What factors would make you choose client-side data fetching over server-side methods?
- How does data freshness requirement influence your data fetching method choice?
- When would you recommend a hybrid approach combining multiple data fetching methods?
- How do SEO requirements affect your data fetching strategy?
- What role does data sensitivity/user-specificity play in method selection?
- How would you handle a page that needs both SEO and real-time updates?
- What are the performance trade-offs between different data fetching methods?
- How do build time considerations affect your choice between SSG/ISR and SSR?
- How would you explain the decision matrix for selecting data fetching methods to a junior developer?
-
Which data fetching method provides the best performance for static content? a)
getServerSidePropsb)getStaticPropswithoutrevalidatec)getStaticPropswithrevalidate: 60d) Client-side withuseEffectAnswer
-
Which method should you use for user-specific data that requires authentication on each access? a) SSG with
getStaticPropsb) ISR withgetStaticPropsandrevalidatec) SSR withgetServerSidePropsd) Client-side withuseContextAnswer
-
For a blog that updates content twice daily, which approach is most appropriate? a) SSG with daily rebuilds b) ISR with 12-hour revalidate c) SSR with no caching d) Client-side polling every hour
Answer
-
Which scenario is LEAST suitable for pure client-side data fetching (useEffect/SWR)? a) User settings page after login b) Product catalog for search engines c) Interactive data visualization d) Chat application message list
Answer
-
When building an internationalized marketing site, which approach balances SEO and localization needs? a) SSG with language-specific builds b) ISR with language fallback c) SSR with language detection d) CSR with language detection
Answer
Practice Exercise
Section titled “Practice Exercise”- Create a new Next.js project called
method-selection-exercise - Create three different pages showcasing different data fetching methods:
/static: A marketing homepage using SSG (getStaticProps)/news: A news article page using ISR (getStaticPropswithrevalidate)/dashboard: A user dashboard using SSR (getServerSideProps)
- For each page, implement appropriate data fetching that matches the method’s strengths:
- Static: Fake data that never changes
- News: Simulate hourly updates with revalidate
- Dashboard: Simulate personalized data requiring auth
- Add a comparison table showing:
- Expected TTFB for each method
- SEO suitability
- Implementation complexity
- Typical use cases
- Style the pages using CSS Modules
- Test each method and verify the behavior matches expectations
Mini Project
Section titled “Mini Project”Build a content aggregator website that demonstrates proper data fetching method selection:
- Create a Next.js project that aggregates content from multiple sources:
- Static documentation (changes monthly)
- News feeds (update hourly)
- User profiles (personalized, change on interaction)
- Live statistics (update every second)
- Search results (query-dependent)
- For each content type, select the appropriate data fetching method:
- Static documentation: SSG with monthly rebuild trigger
- News feeds: ISR with 15-minute revalidate
- User profiles: SSR with authentication checks
- Live statistics: Hybrid SSR/SSG initial state + CSR/WebSocket updates
- Search results: SSR with query parameters
- Implement a cohesive UI with:
- Consistent navigation and styling
- Error handling appropriate to each method
- Loading states where applicable
- Empty states for no data
- Add analytics to track:
- Page load times by method
- Error rates by method
- User engagement by content type
- Optimize for:
- SEO on public-facing content
- Performance under load
- Development maintainability
- Document your method selection decisions in a
DECISIONS.mdfile - Deploy to Vercel and test in production environment
- Gather feedback and iterate on method selections as needed