Disable hydration / only partially hydrate a Next.js app
Asked Answered
T

2

21

Is it possible to enforce an SSR-only mode in Next.js and only partially hydrate the page? Let's say I have this app:

components/dynamic.jsx

export default () => (
  <button onClick={() => console.log("I have been clicked")}>Click me</button>
)

pages/index.jsx

import DynamicComponent from "../components/dynamic.jsx";

export default () => (
  <div>
    <h1>Hello World</h1>
    <p>Lorem ipsum</p>
    <Hydrate>
      <DynamicComponent />
    </Hydrate>
  </div>
);

Now assume we are rendering pages/index.jsx with Next.js so it will be rendered on the server and fully hydrated on the client. For performance reasons (shrink bundle size, reduce execution time) and to have the app play nicer with ads (πŸ˜’) I only want to hydrate the DynamicComponent on the client and at best only load the JavaScript for the DynamicComponent to the client.

Ist that possible with

  • React?
  • Next.js?

Thanks

Told answered 28/3, 2019 at 8:39 Comment(5)
Place a check for process.browser for components you want rendered only on the client. Does that work? – Schilt
spring-media is workign on this, apparently: github.com/spring-media/next-super-performance – Crosstie
@Crosstie that is in fact... me :) – Told
@lukas ;) for now, I'm also happy to have API calls on dynamic pages tamed https://mcmap.net/q/660716/-how-can-i-use-getinitialprops-only-during-the-nextjs-site-build – Crosstie
Yeah, also something like this seems to becoming part of React itself addyosmani.com/blog/rehydration – Told
O
22

You can do it with a hack:

<>
  <StaticContent>
    <h1>Hello World</h1>
    <p>Lorem ipsum</p>
  </StaticContent>
  <DynamicComponent />
</>

And the StaticContent component:

import { createElement, useRef, useState, useEffect } from 'react'

function useStaticContent() {
  const ref = useRef(null)
  const [render, setRender] = useState(typeof window === 'undefined')

  useEffect(() => {
    // check if the innerHTML is empty as client side navigation
    // need to render the component without server-side backup
    const isEmpty = ref.current.innerHTML === ''
    if (isEmpty) {
      setRender(true)
    }
  }, [])

  return [render, ref]
}

export default function StaticContent({ children, element = 'div', ...props }) {
  const [render, ref] = useStaticContent()

  // if we're in the server or a spa navigation, just render it
  if (render) {
    return createElement(element, {
      ...props,
      children,
    })
  }

  // avoid re-render on the client
  return createElement(element, {
    ...props,
    ref,
    suppressHydrationWarning: true,
    dangerouslySetInnerHTML: { __html: '' },
  })
}

It loads the javascript anyway, but does not re-execute it, i.e. it does not do the hydration of these components.

Update: If you use Next.js it is better to use the App folder that already supports partial hydration with the server components and client components.

Orrery answered 23/2, 2020 at 19:8 Comment(7)
That doesn't look like a hack. I think someone would even want to use it as a package. – Boutique
Feel free to put it on a package – Orrery
did a bit of testing, and although it does stop the hydration, apparently nextjs still loads the chunk - you can still see it in the network. Maybe this solution can help, but I haven't tested it yet: scriptedalchemy.medium.com/… – Rabbi
Give this guy a trophy – Elegiac
Does not work, the hydration data are still injected at ` <script id="NEXT_DATA" type="application/json">` – Greysun
@KristiJorgji are two different things. It loads the javascript anyway, but does not re-execute it, i.e. it does not do the hydration. Anyway this comment is old, if you use Next.js it is better to use the App folder that already supports partial hydration with the server components and client components. – Orrery
@AralRoca I used last Next with App Router and still did not work with partial hydration for me. If you have any article please forward me so I check how to implement it – Greysun
M
0

For this error you can simply use this trick : I'm changing css class from module file and then taking the Hydration error. In fact you will render the component in react side same as user intraction.

import cl from "./ButtonIno.module.scss";
import {useEffect, useState} from "react";
const ButtonIno = ({children,type}) => {
    const [show,setShow] = useState(false);
    let styles = {}
    useEffect(()=>{
        setShow(true);
    },[]);
    return (
        <>
            {show &&
            <div className={cl[type]}>
                {/*<div>*/}
                <a href="" >
                    {children}
                </a>
            </div>
            }
        </>
    );
};
export default ButtonIno;
Migrate answered 29/9, 2022 at 8:14 Comment(0)

© 2022 - 2025 β€” McMap. All rights reserved.