NextJS server side renders even without getXProps? - next.js

This code is in a page file in NextJS. Although I'm not using getStaticProps or getServerSideProps it still performs server side rendering.
Is this by design? The docs would imply that these get functions are required: https://nextjs.org/docs/basic-features/data-fetching
import React from "react";
import Head from "next/head";
import Link from "next/link";
import { ApolloProvider } from "#apollo/react-hooks";
import ApolloClient from "apollo-boost";
import { gql } from "apollo-boost";
import { useQuery } from "#apollo/react-hooks";
const client = new ApolloClient({
uri: "https://48p1r2roz4.sse.codesandbox.io",
});
const EXCHANGE_RATES = gql`
{
rates(currency: "USD") {
currency
rate
}
}
`;
const Home: React.FC = () => {
const { loading, error, data } = useQuery(EXCHANGE_RATES);
if (loading) {
return (
<div>
<p>Loading</p>
</div>
);
}
if (error) {
return (
<div>
<p>Error</p>
</div>
);
}
return (
<div>
<ul>
{data.rates.map((item) => (
<li key={item.currency}>
{item.currency} - {item.rate}
</li>
))}
</ul>
</div>
);
};
export default () => (
<ApolloProvider client={client}>
<Home />
</ApolloProvider>
);

Production mode
The page provided has no getInitialProps or getServerSideProps. It's statically optimized (pre-rendered at build time).
So, when you request this page in a browser, you will see a pre-rendered HTML content in the response. Disabling JavaScript does not affect it.
If you navigate to the page by using client-side next/link or router the page will be rendered at client-side without making a request to a server.
Development mode
In Development mode this page would be both - server-side rendered and client-side rendered.
If you'd request the page by typing address in a browser it will be pre-rendered at server-side.
If you navigate to the page by using client-side next/link or router the page will be rendered at client-side without making a request to a server (you will see only Webpack Hot Module Replacement request).

Related

nextjs reload page with Link component

I have a simple nextjs 13 application (appDir) with 2 pages and a <Link/> component navigation.
first page Home - static
second page Test - receiving dynamic random content on the server side (fetch) from a third-party source.
Problem: When the application is rendered everything works fine, but when I try to switch between pages, my test page shows the old content, I can refresh the browser to get the actual data, is very similar to navigating through regular links <a/>, but i need without reloading the entire application.
Q: How can I force nextjs 13 to reload the Test page when I switch between pages with <Link/> component?
// src/components/navbar.tsx
'use client'
import {usePathname} from "next/navigation";
import Link from "next/link";
const navItems = [
{text: 'Home', href: '/'},
{text: 'Test', href: '/test'}
];
const Navbar = () => {
const pathname = usePathname();
return <nav className="nav nav-masthead justify-content-center float-md-end">
{navItems.map((item: { text: string, href: string, link?: boolean }, idx: number) => (
<Link key={idx} href={item.href} className={`nav-link${item.href === pathname ? ' active' : ''}`}>
{item.text}
</Link>
)
)}
</nav>
}
export default Navbar;
// src/app/test/page.tsx
import * as crypto from "crypto";
const getData = async () => {
const res = await fetch('http://localhost:3000/random-data', {cache: 'no-store'});
if (!res.ok) {
throw new Error('Failed to fetch data');
}
return res.json();
}
export default async function Page() {
return <p>{crypto.createHash('sha256').update(JSON.stringify(await getData())).digest('hex')}</p>
};
I've recently asked about this same topic on their feedback discussion on github: https://github.com/vercel/next.js/discussions/41745?sort=new#discussioncomment-4620262
The cause of the problem is Link is only doing client side navigation and seems to serve a cached state of the previously visited component. You'll notice that the client never calls back to the server and thus the server component never runs the second time.
I've been searching for days, but haven't found a way to force Link to reload or force refresh the component to re-render.
My conclusion is that if you have dynamic data that needs to refreshed periodically, it's best to render it in a client component and not use a server component for now.
Also, if you'd like to use Suspense, you'll need to use a library like SWR or React Query for any client side data fetching.

How can I avoid prop drilling with a headless CMS? The context API I think hurts SEO

I want to use a headless CMO in my NextJs app (e.g. Sanity.io).
The content is especially important for SEO.
If I see it correctly, I can only receive the data on page-level via getStaticProps server-side to pre-render it that way (important for SEO).
If I now want to send the data from the page component to a deeply nested child, it's awkward via prop drilling.
My first thought was to use React's Context API (see code).
However, I suspect that during the build the state of the Context API does not take over the values (The SEO text for example).
So the pre-rendered page does not have the SEO content of the headless CMO.
Is there a way to send the values of the headless CMO to deeply nested children via getStaticProps without prop drilling? Or is the context API ok for this in terms of SEO / pre-render?
//pages/index.js
export default function Home({textFromGetStaticProps}) {
const value = useAppContext();
let {seotext, setSeotext} = value.content;
console.log("The State is currently: " + seotext);
console.log("The value of getStaticProps is currently: " + textFromGetStaticProps);
//Can not set this in useEffect since only runs on ClientSide!
setSeotext(() =>{
console.log("---> setCount läuft");
return textFromGetStaticProps;
})
return (
<div className={styles.container}>
<main className={styles.main}>
<h1 className={styles.title}>
The SEO Text is {seotext}
</h1>
</main>
</div>
)
}
//Fetch headless CMO Date via getStaticProps
export async function getStaticProps(context) {
console.log("I am running Static Props");
//API Fetch of headless CMO
return {
props: {textFromGetStaticProps: "SEO Text aus StaticProps"}, // will be passed to the page component as props
}
}
//appContext.js
const AppContext = createContext();
export function AppWrapper({ children }) {
const [seotext, setSeotext] = useState("SEO Text");
const test = {seotext, setSeotext}
console.log("I am Running AppContext: " + test);
return (
<AppContext.Provider value={{
content: test,
}}>
{children}
</AppContext.Provider>
);
}
export function useAppContext() {
return useContext(AppContext);
}
```
Future versions of Next.js will make this much easier but in the meantime you could try using SWR or React Query to fetch data for pre-rendering and then query inside nested components.
There are a few ways to achieve this. The first that comes to mind would be to use something like the React Context API. However, you could also use something like Redux.
Here is an example using the Context API:
import React, { createContext, useContext, useState } from 'react';
const AppContext = createContext();
export function AppWrapper({ children }) {
const [seotext, setSeotext] = useState("SEO Text");
const test = {seotext, setSeotext}
console.log("I am Running AppContext: " + test);
return (
<AppContext.Provider value={{
content: test,
}}>
{children}
</AppContext.Provider>
);
}
export function useAppContext() {
return useContext(AppContext);
}

Next.js SSG page component gets new object from props every time it renders

I'm using Next.js and i want to build simple SSG page, which take immutable data from db on build time and render it to some list of elements. For that purpose i'm using getStaticProps. Here's source code of this page:
import Layout from '../components/Layout'
import utilStyles from '../global-styles/utils.module.css'
import { getSortedPostsData } from '../utils/posts'
import { useMemo } from 'react'
const Home = ({ allPostsData }) => {
const posts = useMemo(() => {
console.log('useMemo log');
return allPostsData.map(({ id, date, title }) => (
<li className={utilStyles.listItem} key={id}>
{title}
<br />
{id}
<br />
{date}
</li>
))
}, [allPostsData])
return (
<Layout home>
<ul className={utilStyles.list}>
{posts}
</ul>
</Layout>
)
}
export const getStaticProps = async () => {
console.log('getStaticProps run')
const allPostsData = await getSortedPostsData()
return {
props: {
allPostsData
}
}
}
export default Home
In production build I can see 'getStaticProps run' log once in console. All required data gets from database and passes to Home component in props. This component renders posts list fine. When i switch to this page in my browser first time I can see exactly one 'useMemo log' in the console. But...
Every time I switch to another page and then back to Home page I see another 'useMemo log' in the browser console too. I'm not understand why this is happening. This page loaded exactly once (not on every page switch). Data for this page is obtained exactly once (in build time). But Next.js passes new array (array with new address) in Home page props every time it renders. Why this is happening and how can i avoid this behavior to memoize my posts list and not render it every time I switch the page?

Next.js withPageAuthRequired with getStaticProps

According documentation #auth0/nextjs-auth0 we can use withPageAuthRequired for trigger login screen on pages required login.
short variant: export const getServerSideProps = withPageAuthRequired();
But what to do if I need to use getStaticProps for pre-render page at build time which can't be used together with getServerSideProps? Is there any way to use withPageAuthRequired on request static generated pages?
Right now I am using double check on client side for check auth. But I would rather use a server side check as i use on other pages.
P.S. There is way to use withPageAuthRequired on client side as well. This is not suitable for my use
Since getStaticProps() is used to build a static page (i.e., no server-side logic/rendering at request time), the auth check and redirect to login will have to happen on the client side.
You might be able to get the behaviour you want by sticking a proxy in front of the static resource (e.g., using Lambda#Edge), though I'm not very familiar with this approach yet.
From your question it sounds like you are already familiar with how to do the check/redirect on the client side, but for the benefit of others who come across this post in the future:
To fetch user information on the client side, add a <UserProvider> to your app, and call the useUser() hook in client-side components.
See docs:
Wrap your pages/_app.js component with the UserProvider component:
// pages/_app.js
import React from 'react';
import { UserProvider } from '#auth0/nextjs-auth0';
export default function App({ Component, pageProps }) {
return (
<UserProvider>
<Component {...pageProps} />
</UserProvider>
);
}
You can now determine if a user is authenticated by checking that the
user object returned by the useUser() hook is defined. You can
also log in or log out your users from the frontend layer of your
Next.js application by redirecting them to the appropriate
automatically-generated route:
// pages/index.js
import { useUser } from '#auth0/nextjs-auth0';
export default function Index() {
const { user, error, isLoading } = useUser();
if (isLoading) return <div>Loading...</div>;
if (error) return <div>{error.message}</div>;
if (user) {
return (
<div>
Welcome {user.name}!
Logout
</div>
);
}
return Login;
}
For other comprehensive examples, see the EXAMPLES.md
document.
An alternative approach that uses withPageAuthRequired() on the client side:
import React from 'react';
import { withPageAuthRequired } from '#auth0/nextjs-auth0';
import Layout from '../components/layout';
export default withPageAuthRequired(function Profile({ user }) {
return (
<Layout>
<h1>Profile</h1>
<h4>Profile</h4>
<pre data-testid="profile">{JSON.stringify(user, null, 2)}</pre>
</Layout>
);
});
Linked from additional examples.

Why I don't see div in html (view page source in browser) when I use getStaticProps in next.js

I try to make test SSR app based on Next.js + React + Apollo.
But I cannot understand how SSR works.
I read the docs and I found that to get some important data while first SSR render we need use getStaticProps and the result of function call will be passed to the props of component which I try to render.
Depends on this props I can render whatever I want, for example I have next code:
import React from "react";
import {GetServerSideProps, GetStaticProps} from "next";
const Home = (props) => {
return (
<div>{props.ValFromGetStaticProps}</div>
)
};
export const getStaticProps: GetStaticProps = async (context) => {
return { props: {ValFromGetStaticProps: 'ValFromGetStaticProps!'} }
};
export default Home;
I expect to see rendered this code on the server side, and if I open sources of the HTML I should see this div. But instead I see just some props object... And there is no div ((
Remark: in the DOM this div present, but for SEO purpose this div should exists in the page source.
Ohh... I didn't see it because I blocked first render early in the parent component:
_app.tsx
export default function ({ Component, pageProps }) {
<BackendDataProvider>
<Component {...pageProps}>
</BackendDataProvider>
}
in the BackendDataProvider was condition
const {data, error, loading} = useQuery(BACKEND_QUERY)
if (error || loading) {
return null;
}
so that was a reason why first render was not rendered correctly

Resources