Loading...
Nextjs 14 Part4
Author Cloudapp
E.G.

Next.js 14 - Complete Example - Custom 404 Page / Loading UI / TailwindCss (ExtraColors) — Part 4

March 23, 2024
Table of Contents

In Part 1 we created the foundation of our Next.js 14 project. In Part 2 we created the frontend with Contentful as headless CMS, Tailwindcss for styling, and Typescript for the coding. In Part 3 we added a header and a footer component, as well as the so-called draft/preview functionality offered by Contentful, and last but not least the nowadays mandatory dark mode toggle. In Part 4 we are going to add a custom 404 page, a loading UI (SVG Spinner) and we add some extra colors to our tailwind.config.ts

Here already a spoiler to the final result of Part 4 -> https://nextjs14-full-example-404-loading.vercel.app/

Spoiler
Spoiler

Custom 404 / Not Found Page

Let’s create a new file “not-found.tsx” under /src/app with our custom wording/styling

import { Container } from "@/components/contentful/container/Container";

function NotFoundPage() {
  return (
    <>
      <div className="min-h-full px-4 py-4  sm:px-6 sm:py-24 md:grid md:place-items-center lg:px-8">
        <div className="mx-auto max-w-max">
          <Container className="mt-5">
            <div className="flex mt-6">
              <p className="text-4xl font-extrabold text-blue600 sm:text-5xl">
                Ups
              </p>
              <div className="ml-6">
                <div className="pl-6 border-l border-gray500">
                  <h2 className="text-3xl font-bold tracking-tight text-gray900 dark:text-white sm:text-4xl">
                    Something went wrong! - Etwas ist schief gelaufen !
                  </h2>
                  <p className="mt-1 text-lg text-gray500 dark:text-white">
                    Please select a topic from the tag cloud above or go back
                    home
                  </p>
                  <p className="mt-1 text-lg text-gray500 dark:text-white">
                    Bitte wählen Sie ein Thema aus der Cloud oben oder klicken
                    auf Go back home Button
                  </p>
                </div>
                <div className="flex mt-10 space-x-3 sm:pl-6">
                  <a
                    href="/"
                    className="inline-flex items-center px-6 py-4 text-sm text-4xl font-medium text-white bg-blue600 border border-transparent rounded-md shadow-sm hover:bg-blue700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue500"
                  >
                    Go back home
                  </a>
                </div>
              </div>
            </div>
          </Container>
        </div>
      </div>
    </>
  );
}

export default NotFoundPage;

If you click “About” in the upper left corner of https://nextjs14-full-example-404-loading.vercel.app/ you will see our custom 404 page.

404 page status 404
404 page status 404

Instead of the default one

default 404
default 404

Custom UI Loading

Let’s go ahead with the new file loading.tsx, which I created under /src/app

export default function loading() {
  return (
    <div className="pt-4 text-center">
      <div role="status">
        <svg
          aria-hidden="true"
          className="inline w-10 h-10 mr-2 text-gray200 animate-spin dark:text-gray600 fill-blue600"
          viewBox="0 0 100 101"
          fill="none"
          xmlns="http://www.w3.org/2000/svg"
        >
          <path
            d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
            fill="currentColor"
          />
          <path
            d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
            fill="currentFill"
          />
        </svg>
        <span className="sr-only">Loading...</span>
      </div>
    </div>
  );
}

Since the loading spinner is only visible during the initial loading state, I created my subpage called “spinner” -> https://nextjs14-full-example-404-loading.vercel.app/spinner so that you can see it in action.

loading spinner
loading spinner

The special file loading.tsx helps you create meaningful Loading UI with React Suspense. With this convention, you can show an instant loading state from the server while the content of a route segment loads. The new content is automatically swapped in once rendering is complete. It’s a great possibility to enhance the UX.

Extra Colors in tailwind.config.ts

I added these colors in the “theme” section within tailwind.config.ts

 white: extraColors.white,
 black: extraColors.black,
 indigo: extraColors.indigo,
console.log("extracolors", extraColors);

shows the color names with their corresponding “Hex-Value”, which I imported from the package “tailwindcss/colors”

ExtraColors Tailwind
ExtraColors Tailwind

I imported other colors from the package “@contentful/f36-tokens” as well.

import tokens from "@contentful/f36-tokens";

const colors = Object.entries(tokens).reduce(
  (acc: Record<string, any>, [key, value]) => {
    // Filter Hex colors from the f36-tokens
    if (/^#[0-9A-F]{6}$/i.test(value as any)) {
      acc[key] = value;
    }

    return acc;
  },
  {} as Record<string, string>
);
console.log("colors", colors);
console.log colors
console.log colors

Complete tailwind.config.ts with the integration of “extracolors” from tailwindcss/colors and “colors” via tokens from “@contentful/f36-tokens”

import type { Config } from "tailwindcss";
import tokens from "@contentful/f36-tokens";
const { fontFamily } = require("tailwindcss/defaultTheme");
const extraColors = require("tailwindcss/colors"); //for extra colors e.g. in the Arctile TOC -> emerald

const colors = Object.entries(tokens).reduce(
  (acc: Record<string, any>, [key, value]) => {
    // Filter Hex colors from the f36-tokens
    if (/^#[0-9A-F]{6}$/i.test(value as any)) {
      acc[key] = value;
    }

    return acc;
  },
  {} as Record<string, string>
);

const config: Config = {
  darkMode: "class",
  content: [
    "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    colors,
    extend: {
      colors: {
        emerald: extraColors.emerald,
        white: extraColors.white,
        black: extraColors.black,
        indigo: extraColors.indigo,
      },
      maxWidth: {
        "8xl": "90rem",
      },
      letterSpacing: {
        snug: "-0.011em",
      },
      boxShadow: {
        // light
        "tremor-input": "0 1px 2px 0 rgb(0 0 0 / 0.05)",
        "tremor-card":
          "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)",
        "tremor-dropdown":
          "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)",
      },
      borderRadius: {
        "tremor-small": "0.375rem",
        "tremor-default": "0.5rem",
        "tremor-full": "9999px",
      },
      fontSize: {
        "2xs": "0.625rem",
        "3xl": "1.75rem",
        "4xl": "2.5rem",
        "tremor-label": "0.75rem",
        "tremor-default": ["1.0rem", { lineHeight: "1.25rem" }],
        "tremor-title": ["1.125rem", { lineHeight: "1.75rem" }],
        "tremor-metric": ["1.875rem", { lineHeight: "2.25rem" }],
      },
      lineHeight: {
        tighter: "1.1",
      },
      fontFamily: {
        sans: ["var(--font-urbanist)", ...fontFamily.sans],
      },
    },
  },
  plugins: [require("@tailwindcss/typography")],
};
export default config;

Since the names of the colors were not “gray-600” or “blue-600”, but “gray600” and “blue600” without the dash I had to fix the className styles in my tsx files. That’s the reason why a lot of files were modified. So let’s sum up.

  1. Changed/added files

    Modified Files
    Modified Files

  2. Vercel project URL ->

https://nextjs14-full-example-404-loading.vercel.app/

Github Repo with full code

https://github.com/cloudapp-dev/nextjs14-typescript-app-router-contentful/tree/nextjs14-part4

If you like what you see, then please support me with a clap or follow me on medium.com.

Related articles