Next.js: Defer getServerSideProps on client-side navigation

Created on 8 Jun 2020  路  3Comments  路  Source: vercel/next.js

Feature request

Is your feature request related to a problem? Please describe.

When clicking in a link to a page that uses getServerSideProps, the navigation will be kept in hold until the server response is complete, then the navigation will start. I want to be able to defer the getServerSideProps data-fetching until the client-side navigation has been completed. Similar to the behavior of getStaticProps with fallback: true.

Describe the solution you'd like

  • When directly accessing the page (e.g., typing the URL and pressing Enter), it should be server-side rendered
  • When navigating to the page from another page, the navigation should happen immediately. The page will be rendered with a loading state. When the navigation is completed, the gSSP request should be fired to fetch the content and send it through the page props.

I should be able to opt-in this behavior with a flag in the return of gSSP. The router should have a flag to tell if the page is fetching data:

// pages/example.js

function Post({ post }) {
  const router = useRouter()

  // If the page data is not yet fetched, this will be displayed
  // initially until getServerSideProps() finishes running
  if (router.isFetchingAfterDefer) {
    return <div>Loading...</div>
  }

  // Render post...
}

export async function getServerSideProps(context) {
  return {
    props: { ... },
    deferOnClientSideNavigation: true,
  }
}

Describe alternatives you've considered

Using getInitialProps/getServerSideProps to detect if it is client-side navigation and send empty props, then repeat the fetch logic in the component code

story feature request

Most helpful comment

Hi
I'm working an next js these days and i want to do something cool. when user make http request to my page i (for SEO) i want page render in Server Side and show all data at ones (i want fetch data at Server Side). but when page load completed and user navigate between pages using next/lint i want data render in Client Side (i want fetch data in Client Side). with getInitialProps we can do this let me show you an example.

function Index({ posts }) {
  const [postsArr, setPostsArr] = useState(posts);

  useEffect(() => {
    async function fetchNewPosts() {
      const res = await fetch("https://getpost.com/");
      setPostsArr(await res.json());
    }

    if (posts.length === 0) {
      fetchNewPosts();
    }
  }, []);

  return (
    <>
      <Nav />
      <p>Hello World May I Pick Banana!</p>
      <pre>{JSON.stringify(postsArr, null, 4)}</pre>
    </>
  );
}

Index.getInitialProps = async ({ req }) => {
  let posts = [];
  if (req) {
    const res = await fetch("https://getpost.com/");
    posts = await res.json();
  }
  return { posts };
};

export default Index;

in this approach if there is request we fetch data at first place (Server Side Rendering) but if we navigating to this page with next/link it fetches data from client side (Client Side Rendering).
But with these new data fetching methods getStaticProps and getServerSideProps we cant able to do above behavior.
please fix this issue in new versions.

All 3 comments

Hi
I'm working an next js these days and i want to do something cool. when user make http request to my page i (for SEO) i want page render in Server Side and show all data at ones (i want fetch data at Server Side). but when page load completed and user navigate between pages using next/lint i want data render in Client Side (i want fetch data in Client Side). with getInitialProps we can do this let me show you an example.

function Index({ posts }) {
  const [postsArr, setPostsArr] = useState(posts);

  useEffect(() => {
    async function fetchNewPosts() {
      const res = await fetch("https://getpost.com/");
      setPostsArr(await res.json());
    }

    if (posts.length === 0) {
      fetchNewPosts();
    }
  }, []);

  return (
    <>
      <Nav />
      <p>Hello World May I Pick Banana!</p>
      <pre>{JSON.stringify(postsArr, null, 4)}</pre>
    </>
  );
}

Index.getInitialProps = async ({ req }) => {
  let posts = [];
  if (req) {
    const res = await fetch("https://getpost.com/");
    posts = await res.json();
  }
  return { posts };
};

export default Index;

in this approach if there is request we fetch data at first place (Server Side Rendering) but if we navigating to this page with next/link it fetches data from client side (Client Side Rendering).
But with these new data fetching methods getStaticProps and getServerSideProps we cant able to do above behavior.
please fix this issue in new versions.

@sadraromexs

That's exactly what I've done in my previous 2 NextJS projects. Since starting my new project I've taken the official advice and am doing everything using getServerSideProps (because I believe the current model for getStaticProps is totally broken).

My old sites were lightning fast, the new one is significantly slower, because making a trip to the API on every request is obviously going to be slower. Seems like a bad decision.

It's obvious that user should not wait a few seconds in which nothing happens (because getServerSideProps is not finished loading) when he clicks a link. He should see some action is happening, for example:

  • Loading spinner
  • Data template (boxes for images, text and so on), youtube example.

Just one question, how then you will skip graphql parallel query with router.isFetchingAfterDefer?

export function Test(): JSX.Element {
  const { data, error } = useTestQuery() // will it fire alongside getServerSideProps?

  return (
    <Main>
          {JSON.stringify(data)}
    </Main>
  )
}

export const getServerSideProps: GetServerSideProps = async () => {
  const apolloClient = initializeApollo()

  await apolloClient.query({ query: TestQuery })

  return {
    props: {
      initialApolloState: apolloClient.cache.extract(),
    },
  }
}
Was this page helpful?
0 / 5 - 0 ratings