Pre-rendering and next.js

Pre-rendering

pre-rendering: on first page load the browser receives pre-rendered HTML to speed up loading / displaying

advantages:

  • faster initial rendering
  • reduces additional API calls on the client
  • easier indexing by search engines

Pre-rendering

example:

Disable JavaScript in your browser's developer tools settings and visit reactjs.org - you will still see content, though some interactivity will not work (e.g. dropdowns)

Approaches

  • static site generation (pre-rendering static content)
  • server-side rendering (pre-rendering dynamic content)

Static site generation

  • makes sense for data that changes infrequently (e.g. blog posts)
  • when data changes the site has to be regenerated statically
  • data that changes frequently (e.g. comments on a blog post) would not be part of the pre-rendering

Server-side rendering

  • when a React page is opened a prerendered version of it is created on the server and sent to the client
  • requires JavaScript on the server (node.js)

Server-side rendering and data fetching

usual process for fetching data in a React app:

  • React app is sent to the client
  • app renders initially with no data
  • client requests additional data
  • data is sent to the client
  • app re-rendes

process with server-side rendering:

  • data is fetched on the server
  • app is rendered on the server
  • pre-rendered app and the corresponding data for making it dynamic are sent to the client

Tools

  • gatsby.js: static site generation
  • next.js: static site generation, server-side rendering

Next.js

Next.js

React framework that supports:

  • static site generation
  • server-side rendering
  • simple file-based routing
  • backend / API development in the same project (for simple projects)

Resources

The next.js website has great materials: https://nextjs.org

Create-next-app

Create-next-app

npx create-next-app@latest
npx create-next-app@latest --ts

Project structure

possible project structure:

  • pages
  • public: static assets
  • styles
  • components

Development server

npm run dev

Deployment with node.js

to build a production version:

npm run build

to run it (on the server):

npm run start

Deployment to a static server

possible only if no server-side rendering is needed (i.e. no getServerSideProps)

change the build script in package.json to: next build && next export

then build the static version (to the out folder) via:

npm run build

(see https://nextjs.org/learn/excel/static-html-export)

Pages and navigation

Pages and navigation

Example page definition:

// pages/index.tsx
import type { NextPage } from 'next';
import Link from 'next/link';
import Head from 'next/head';

const Index: NextPage = () => (
  <div>
    <Head>
      <title>Home</title>
    </Head>
    <Link href="/about">
      <a>About Page</a>
    </Link>
    <p>Hello Next.js</p>
  </div>
);

export default Index;

Pages and navigation

special components provided by next:

  • Link: link inside a single-page application
  • Head: enables easily setting content of the page's <head> element

Shared content

defining "global" content that is shared across all pages:

// _app.tsx
function MyApp({ Component, pageProps }: AppProps) {
  return (
    <div>
      {/* fixed header content */}
      <header>header content</header>
      {/* variable content - defined by the route */}
      <Component {...pageProps} />
      {/* fixed footer content */}
      <footer>footer content</footer>
    </div>
  );
}

Pages and navigation

Task: create further pages

Pre-rendering and data fetching

Pre-rendering and data fetching

in next.js we can define code that is executed at several different points:

  • code that runs when building
  • code that runs on the server
  • code that runs in the browser

Pre-rendering and data fetching

when building, next.js will pre-render all entries in the pages directory

some pages are pre-rendered without any additional input, others might be connected to data sources

Pre-rendering and data fetching

special (asynchronous) functions that we can export from a page definition file:

  • getServerSideProps (runs every time a page is requested)
  • getStaticProps (runs during build)

can load data and pass them into the page components as props

are only executed on the build machine or server respectively → may read files or make direct database queries

The functionality of fetch will be polyfilled automatically

Pre-rendering

example: a page that loads a random joke from an API

import type { GetServerSideProps, NextPage } from 'next';

type JokeProps = { joke: string };
const Joke: NextPage<JokeProps> = (props) => (
  <article>{props.joke}</article>
);
const getServerSideProps: GetServerSideProps<JokeProps> = async () => {
  const url = 'https://api.chucknorris.io/jokes/random';
  const res = await fetch(url);
  const joke = (await res.json()).value;
  return { props: { joke: joke } };
};

export default Joke;
export { getServerSideProps };

Pre-rendering

exercises:

Route parameters

Route parameters

we can query parameters from URLs like these:

  • /todos/3
  • /todos/?todoid=3

Route parameters

Route parameters are enclosed in square brackets in the file name, e.g. pages/todos/[id].js

Route parameters

Dynamically querying route parameters:

import { NextPage } from 'next';
import { useRouter } from 'next/router';

const TodoDetails: NextPage = () => {
  const router = useRouter();
  return (
    <div>
      <h1>detail view for todo {router.query.id}</h1>
    </div>
  );
};

export default TodoDetails;

Route parameters

pre-rendering pages based on a set of route parameters - via getStaticPaths

export const getStaticPaths: GetStaticPaths = () => {
  const paths = [];
  for (let i = 1; i <= 200; i++) {
    paths.push({ params: { id: String(i) } });
  }
  return { paths, fallback: false };
};

getStaticPaths returns an array describing the routes under the entry paths - the entries are passed to getStaticProps

Route parameters

after implementing getStaticPaths we can pre-render pages with dynamic routes

npm run build