I am using getServerSideProps in pages/post/index.js:
import React from "react";
import Layout from "../../components/Layout";
function Post({ post }) {
console.log("in render", post);
return (
<Layout title={post.name}>
<pre>{JSON.stringify(post, undefined, 2)}</pre>
</Layout>
);
}
export async function getServerSideProps({ query }) {
return fetch(
`${process.env.API_URL}/api/post?id=${query.id}`
)
.then(result => result.json())
.then(post => ({ props: { post } }));
}
export default Post;
When I directly load /post/2 it works as expected but when I go from /posts to /post/2 by clicking on a link:
<Link
as={`/post/${post.id}`}
href={`/post?id=${post.id}`}
>
It looks like nothing happens for 2 seconds (the api delay) and then the content shows. I can see in the network tab that _next/data/development/post/9.json is being loaded by fetchNextData.
I would like to show a loading spinner when I move from one route to another using next/Link but I can't find any documentation on getServerSideProps that allows me to do this.
When I directly go to /post/:id I'd like the data to be fetched server side and get a fully rendered page (works) but when I then move to another route the data should be fetched from the client (works). However; I would like to have a loading indicator and not have the UI freeze up for the duration of the data request.
You can use nprogress in your _app.js
import NProgress from 'nprogress';
import "nprogress/nprogress.css";
import Router from 'next/router';
NProgress.configure({
minimum: 0.3,
easing: 'ease',
speed: 800,
showSpinner: false,
});
Router.events.on('routeChangeStart', () => NProgress.start());
Router.events.on('routeChangeComplete', () => NProgress.done());
Router.events.on('routeChangeError', () => NProgress.done());
or dynamic import to _app.js to reduce bundle size
ProgessBar.js
import Router from 'next/router';
import NProgress from 'nprogress';
import "nprogress/nprogress.css";
NProgress.configure({
minimum: 0.3,
easing: 'ease',
speed: 500,
showSpinner: false,
});
Router.events.on('routeChangeStart', () => NProgress.start());
Router.events.on('routeChangeComplete', () => NProgress.done());
Router.events.on('routeChangeError', () => NProgress.done());
export default function () {
return null;
}
_app.js
import dynamic from 'next/dynamic';
const ProgressBar = dynamic(() => import('components/atoms/ProgressBar'), { ssr: false });
const App = () => {
...
return <>
...
<ProgressBar />
</>
}
Here is an example using hooks.
pages/_app.js
import Router from "next/router";
export default function App({ Component, pageProps }) {
const [loading, setLoading] = React.useState(false);
React.useEffect(() => {
const start = () => {
console.log("start");
setLoading(true);
};
const end = () => {
console.log("findished");
setLoading(false);
};
Router.events.on("routeChangeStart", start);
Router.events.on("routeChangeComplete", end);
Router.events.on("routeChangeError", end);
return () => {
Router.events.off("routeChangeStart", start);
Router.events.off("routeChangeComplete", end);
Router.events.off("routeChangeError", end);
};
}, []);
return (
<>
{loading ? (
<h1>Loading...</h1>
) : (
<Component {...pageProps} />
)}
</>
);
}
**Here is how I did it in NextJs with Material UI and nprogress**
import '../styles/globals.css';
import { useEffect, useState } from 'react';
import Router from 'next/router';
import NProgress from 'nprogress';
import { useStyles } from '../src/utils';
import { CircularProgress } from '@material-ui/core';
NProgress.configure({ showSpinner: false });
function MyApp({
Component,
pageProps
}) {
const classes = useStyles();
const [loading, setLoading] = useState(false);
useEffect(() => {
const jssStyles = document.querySelector('#jss-server-side');
if (jssStyles) jssStyles.parentElement.removeChild(jssStyles);
const start = () => {
console.log('start');
NProgress.start();
setLoading(true);
};
const end = () => {
console.log('findished');
NProgress.done();
setLoading(false);
};
Router.events.on('routeChangeStart', start);
Router.events.on('routeChangeComplete', end);
Router.events.on('routeChangeError', end);
return () => {
Router.events.off('routeChangeStart', start);
Router.events.off('routeChangeComplete', end);
Router.events.off('routeChangeError', end);
};
}, []);
return (
<>
{loading ? (
<div className={classes.centered}>
<CircularProgress size={25} color='primary' />
</div>
) : (
<Component {...pageProps} />
)}
</>
);
}
export default MyApp;