← Back to DevBytes

Server-Side Rendering with Redux: SSR, SSG, ISR

Introduction to Server-Side Rendering with Redux

Modern web development relies heavily on delivering fast, SEO-friendly applications. While Single Page Applications (SPAs) offer a fluid user experience, they often suffer from poor initial load times and suboptimal search engine indexing. This is where server-side rendering (SSR), static site generation (SSG), and incremental static regeneration (ISR) come into play. When combined with a robust state management library like Redux, these rendering strategies allow you to pre-fetch state on the server and deliver a fully rendered application to the client.

What is SSR, SSG, and ISR?

Why Server-Side State Management Matters

When rendering on the server, you cannot rely on client-side lifecycle methods (like useEffect in React) to fetch data. If you do, the server will render an empty state, and the client will have to fetch the data after hydration, negating the benefits of SSR. By integrating Redux on the server, you can dispatch actions to populate the Redux store before the HTML is generated. This state is then serialized and sent to the client, where it is used to hydrate the client-side Redux store seamlessly.

Setting Up Redux for Server-Side Rendering

The most critical rule of server-side Redux is to avoid creating a singleton store. If you export a single store instance, all users hitting the server will share the same state, leading to massive data leaks and cross-request contamination. Instead, you must create a factory function that generates a new store for every request or build process.

Creating a Universal Redux Store

Below is an example of a store configuration using Redux Toolkit and Redux Thunk, designed to be safely instantiated on both the server and the client.

import { configureStore } from '@reduxjs/toolkit';
import rootReducer from './reducers';

export const makeStore = (initialState = {}) => {
  return configureStore({
    reducer: rootReducer,
    preloadedState: initialState,
    middleware: (getDefaultMiddleware) => 
      getDefaultMiddleware({ thunk: true }),
  });
};

Implementing SSR with Redux

To implement SSR, we will use Next.js, as it provides built-in data fetching methods that perfectly align with server-side Redux state population. The getServerSideProps function runs on the server for every request.

Fetching Data on the Server

Inside getServerSideProps, you instantiate a new store, dispatch your async actions, wait for them to resolve, and then pass the final state to the component via props.

import { makeStore } from '../store';
import { fetchUser } from '../store/userSlice';

export async function getServerSideProps(context) {
  const store = makeStore();
  const { id } = context.params;

  // Dispatch the async thunk and wait for it to complete
  await store.dispatch(fetchUser(id));

  // Extract the state to pass it to the client
  return {
    props: {
      initialReduxState: store.getState(),
    },
  };
}

Hydrating State on the Client

Next, you need to configure your application wrapper to use the initialReduxState passed from the server to initialize the client-side store. This process is known as hydration.

import { Provider } from 'react-redux';
import { makeStore } from '../store';

function MyApp({ Component, pageProps }) {
  // Create a store instance using the initial state from the server
  const store = makeStore(pageProps.initialReduxState);

  return (
    <Provider store={store}>
      <Component {...pageProps} />
    </Provider>
  );
}

export default MyApp;

Implementing SSG and ISR with Redux

Static Site Generation and Incremental Static Regeneration follow a very similar pattern to SSR, but they use Next.js's getStaticProps function. The primary difference is when the code runs: SSG runs at build time, and ISR runs at build time but updates periodically based on a revalidate parameter.

Static Site Generation (SSG)

For SSG, the store is created during the build process. The resulting state is baked into the static HTML and JSON files.

import { makeStore } from '../store';
import { fetchProducts } from '../store/productsSlice';

export async function getStaticProps() {
  const store = makeStore();

  // Fetch data at build time
  await store.dispatch(fetchProducts());

  return {
    props: {
      initialReduxState: store.getState(),
    },
  };
}

Incremental Static Regeneration (ISR)

To turn the SSG example into ISR, simply add the revalidate key to the returned object. This tells Next.js to regenerate the page in the background if a request comes in after the specified number of seconds.

export async function getStaticProps() {
  const store = makeStore();

  await store.dispatch(fetchProducts());

  return {
    props: {
      initialReduxState: store.getState(),
    },
    // Regenerate the page at most once every 60 seconds
    revalidate: 60, 
  };
}

Best Practices for Redux SSR

Conclusion

Integrating Redux with Server-Side Rendering, Static Site Generation, and Incremental Static Regeneration bridges the gap between robust client-side state management and high-performance server-rendered applications. By ensuring a new store is created for every server execution and properly hydrating the client with the pre-fetched state, you can deliver lightning-fast, SEO-friendly experiences without sacrificing the architectural benefits of Redux. Whether you need real-time data via SSR or highly cacheable pages via ISR, mastering these patterns is essential for building modern, scalable web applications.

— Ad —

Google AdSense will appear here after approval

← Back to all articles