Have you heard about Hack Frontend Community?Join us on Telegram!

Server-Side Rendering (SSR) in Next.js: How It Works

SSR (Server-Side Rendering) is a strategy where HTML is generated on the server on every user request. Unlike SSG, the page is not created ahead of time but is built in real time.

How It Works

1

User makes a request

The browser sends a request to the server. For example, a user opens their profile page on Hack Frontend.

2

Server renders the page

Next.js runs the server component: accesses the database, checks authorization, collects data and generates HTML.

3

HTML is sent to the client

The user receives ready HTML with up-to-date data. Then React hydrates the page on the client, making it interactive.

SSR in App Router

In the App Router SSR is triggered when a component uses dynamic data. Just add cache: 'no-store' to a fetch call or use dynamic functions (cookies(), headers()):

// app/profile/page.tsx
import { cookies } from 'next/headers'
import { db } from '@/lib/db'

export default async function ProfilePage() {
  const session = cookies().get('session')
  if (!session) return <LoginPrompt />

  const user = await db.user.findUnique({
    where: { sessionId: session.value }
  })

  return (
    <div>
      <h1>Hello, {user.name}!</h1>
      <p>Problems solved: {user.solvedProblems.length}</p>
    </div>
  )
}

Using cookies() automatically makes this page dynamic (SSR). Next.js understands that the result depends on the request and cannot be cached.

Explicit Dynamic Rendering

You can explicitly disable caching for fetch requests:

// app/feed/page.tsx
export default async function FeedPage() {
  const res = await fetch('https://api.hackfrontend.com/feed', {
    cache: 'no-store'
  })
  const posts = await res.json()

  return <FeedList posts={posts} />
}

Or set the mode for an entire route segment:

// app/dashboard/layout.tsx
export const dynamic = 'force-dynamic'

export default function DashboardLayout({
  children
}: {
  children: React.ReactNode
}) {
  return <div className="dashboard">{children}</div>
}

SSR in Pages Router (Legacy)

In the Pages Router SSR uses getServerSideProps:

// pages/profile.tsx
export async function getServerSideProps(context) {
  const { req } = context
  const session = req.cookies.session

  if (!session) {
    return { redirect: { destination: '/auth/login', permanent: false } }
  }

  const user = await db.user.findUnique({
    where: { sessionId: session }
  })

  return { props: { user } }
}

export default function ProfilePage({ user }) {
  return (
    <div>
      <h1>Hello, {user.name}!</h1>
      <p>Problems solved: {user.solvedProblems.length}</p>
    </div>
  )
}

When to Use SSR

Good for:

  • Personalized pages (profile, settings, dashboard)
  • Content depending on cookies or headers (auth, A/B tests)
  • Data that must be current at request time
  • Pages with frequently changing content where ISR is not enough

Not suitable for:

  • Static content (documentation, blog). SSG will be faster and cheaper.
  • Pages where slightly stale data is acceptable. ISR will be more efficient.

SSR vs SSG

SSGSSR
When generatedAt build timeOn every request
Response speedInstant (CDN)Depends on server
Data freshnessAt build timeAlways current
Server loadNoneOn every request
PersonalizationNoYes

Important:

SSR increases Time to First Byte (TTFB) because the server spends time rendering before sending the response. If the page makes heavy database queries, the user will wait. Use SSR deliberately, not by default.

Streaming

In the App Router Next.js supports streaming HTML. This lets you send parts of the page as they become ready without waiting for all requests to finish:

// app/dashboard/page.tsx
import { Suspense } from 'react'

export default function DashboardPage() {
  return (
    <div>
      <h1>Dashboard</h1>
      <Suspense fallback={<p>Loading stats...</p>}>
        <UserStats />
      </Suspense>
      <Suspense fallback={<p>Loading activity...</p>}>
        <RecentActivity />
      </Suspense>
    </div>
  )
}

The user sees the heading and fallbacks immediately while data loads progressively. This significantly improves perceived speed.

Useful Resources

By continuing to use the platform, you accept the terms of the Privacy Policy and the use of cookies.