DEV Community

sadnessOjisan
sadnessOjisan

Posted on

[NextJS] smooth routing when SSR

heavy SSR makes TTFB longer

In NextJS, the heavy getServerSideProps method freeze user transition. For example, If sleep 5sec in getServerSideProps, the user need wait 5 sec after clicking a link.

import Link from "next/link";

export default () => {
  return <Link href="about">heavy about</Link>;
};
Enter fullscreen mode Exit fullscreen mode
export default (props) => {
  return <div>this is about page: {props.id}</div>;
};

export async function getServerSideProps(context) {
  await sleepByPromise(5);
  return {
    props: { id: 1 }, // will be passed to the page component as props
  };
}

function sleepByPromise(sec) {
  return new Promise((resolve) => setTimeout(resolve, sec * 1000));
}
Enter fullscreen mode Exit fullscreen mode

Here is bad UX demo. You should wait 5 sec after clicking a link. This will be bad experience for user.

solution: use router.change

NextJS has a Router.event('eventName', ()=>{}) method. This method can handle events of routing change. Especially "routeChangeStart" can handle the start point of change routing. Then if you toggle state which manages to show loading or not, you can show loader.

import Link from "next/link";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";

export default () => {
  const [isLoading, setLoadingState] = useState(false);
  const router = useRouter();
  useEffect(() => {
    router.events.on("routeChangeStart", (url) => {
      setLoadingState(true);
    });
  }, []);
  return isLoading ? (
    <div
      style={{
        background: "rgba(0,0,0,0.8)",
        color: "white",
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
        fontSize: 32,
        position: "fixed",
        top: 0,
        left: 0,
        width: "100vw",
        height: "100vh",
      }}
    >
      <marquee>loading</marquee>
    </div>
  ) : (
    <Link href="about">heavy about</Link>
  );
};
Enter fullscreen mode Exit fullscreen mode

loading

here is demo.

soure code

https://github.com/ojisan-toybox/next-js-routing-inidicator

Top comments (2)

Collapse
 
seinopsys profile image
David Joseph Guzsik

I just pulled in react-topbar-progress-indicator from NPM and connected it to the same routing events, works equally well and the user is not left waiting with nothing happening

import React, { useEffect, useState } from 'react';
import { Router } from 'next/router';
import TopBarProgress from 'react-topbar-progress-indicator';

TopBarProgress.config({
  barColors: ['rgba(255,0,0,.75)'],
  shadowBlur: 5,
  barThickness: 2,
});

const ProgressIndicator: React.FC = () => {
  const [visible, setVisible] = useState(false);

  useEffect(() => {
    const start = () => void setVisible(true);
    const complete = () => void setVisible(false);

    Router.events.on('routeChangeStart', start);
    Router.events.on('routeChangeComplete', complete);
    Router.events.on('routeChangeError', complete);

    return () => {
      Router.events.off('routeChangeStart', start);
      Router.events.off('routeChangeComplete', complete);
      Router.events.off('routeChangeError', complete);
    };
  }, []);

  return <>{visible && <TopBarProgress />}</>;
};

export default ProgressIndicator;
Enter fullscreen mode Exit fullscreen mode
Collapse
 
julioflima profile image
Julio Lima

Thank for it. useRoutes().events Doesn't work for me.