DEV Community

Cover image for Watch Out For Broken Links, 404 Page With Framer Motion, TailwindCSS and NextJs
Akcanakci
Akcanakci

Posted on

Watch Out For Broken Links, 404 Page With Framer Motion, TailwindCSS and NextJs

Trying to be different isn't easy. We get so accustomed to usual apps, layouts, and colors that it's hard to think of something else.

In any case, here is my take on a different 404 page design. The tools I use are always the same: React/Next.js for the page, Tailwind CSS for styling, and Framer Motion to make it move.

Would you like to skip to the end? You can preview it at my website with the full code ready to grab. Also, there are many more designs! Hope you like it.


First Things First

I assume you know what React/Next.js is and how to install the necessary tools and libraries. I will be writing for Next.js. Here is a brief walkthrough:

  1. Install NextJs or just React
  2. Add TailwindCSS
  3. And finally, add Framer Motion!

Let's Begin

Create a file under the /pages directory and name it 404.js. But you are not limited to 404, of course. You can read about other custom errors here and change the page name with the contents in them accordingly.

After creating the page we need a function.
**404.js**

export default function YouDiedErrorPage() {
 return(...)
}
Enter fullscreen mode Exit fullscreen mode

Name of the function doesn't matter, just name it anything you want.

As you can see in the cover image, I have a text at the center of the screen. So we need a screen and a text!

**404.js**

export default function YouDiedErrorPage() {
  return (
    // Div as the screen
    <div className="grid h-screen w-screen place-items-center overflow-hidden bg-black text-center font-serif">
      {/* A container for the text which is centered */}
      <div className="relative whitespace-nowrap">
        {/* And a text with an id of title */}
        <h1
          id="title"
          className="absolute inset-0 flex items-center justify-center text-5xl text-red-500/40 opacity-0 will-change-transform md:text-7xl"
        >
          YOU <br className="inline-flex md:hidden" />
          DIED
        </h1>
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Whats the point of having framer motion if we are going to have just a single text? You are right! Let's have more text with proper styling and responsive goodness.

**404.js**

export default function YouDiedErrorPage() {
  return (
    // Div as the screen
    <div className="grid h-screen w-screen place-items-center overflow-hidden bg-black text-center font-serif">
      {/* A container for the text */}
      <div className="relative whitespace-nowrap">
        {/* And a text with an id of title */}
        <h1
          id="title"
          className="absolute inset-0 flex items-center justify-center text-5xl text-red-500/40 opacity-0 will-change-transform md:text-7xl"
        >
          YOU <br className="inline-flex md:hidden" />
          DIED
        </h1>
        <a
          href="#you-died-go-home"
          id="respawnText"
          className="absolute inset-0 flex items-center justify-center text-2xl text-neutral-400 opacity-0 transition-colors duration-300 hover:text-red-500 md:text-3xl"
        >
          Click here to respawn ↻
        </a>
        <p
          id="reasonOfDeath"
          className="absolute inset-0 mt-6 flex items-center justify-center text-neutral-500/80 opacity-0 md:mt-8"
        >
          - 404: Death by broken link! -
        </p>
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Have you ever played a Dark Souls-like game? If you fail or die, the "Game Over" (or in our case, "You Died") text fades and scales in. After that, a "Load Game" or "Exit" button appears along with some statistics or other relevant information. This will be the same!

We will show the title and after that a link with "Click here to respawn ↻" text will appear with a "- 404: Death by broken link! -" subtitle.

As you can see we have id's for each element which we will use to make the animations. For that we need framer motion's useAnimate() hook which you can read about it here.

Let's Animate

Framer Motion’s useAnimate hook with async functions allows you to easily sequence animations in any way you want. All you need is a scope to tell Framer Motion where to look and an animate function to specify what to do. Check out the code below:

  const [scope, animate] = useAnimate();

  async function killThem() {
    await animate("#title", { scale: 2, opacity: 1 }, { duration: 3 });
    await animate("#title", { opacity: 0 }, { duration: 1 });
    await animate("#respawnText", { opacity: 1 }, { duration: 1 });
    await animate("#reasonOfDeath", { opacity: 1 }, { duration: 0.7 });
  }
Enter fullscreen mode Exit fullscreen mode

Everything here is pretty self-explanatory. Create an async function with a proper name, and by awaiting every single animation create a sequence. Pick an id and tell it what to do. Amazingly simple! Look at the final code now and you will see what it does. I also added some additional functions which will be useful for development.

**404.js**

import { useAnimate } from "framer-motion";
import { useEffect } from "react";

export default function YouDiedErrorPage() {
  const [scope, animate] = useAnimate();

  async function killHim() {
    await animate("#title", { scale: 2, opacity: 1 }, { duration: 3 });
    await animate("#title", { opacity: 0 }, { duration: 1 });
    await animate("#respawnText", { opacity: 1 }, { duration: 1 });
    await animate("#reasonOfDeath", { opacity: 1 }, { duration: 0.7 });
  }
  // With this we are starting the animation, you can also call the function in anywhere you like!
  useEffect(() => {
    killHim();
  }, []);

  // Function to refresh the page for development and demonstration purposes.
  function handleRespawnClick() {
    window.location.reload();
  }

  return (
    <div className="grid h-screen w-screen place-items-center overflow-hidden bg-black text-center font-serif">
      <div ref={scope} className="relative whitespace-nowrap">
        <h1
          id="title"
          className="absolute inset-0 flex items-center justify-center text-5xl text-red-500/40 opacity-0 will-change-transform md:text-7xl"
        >
          YOU <br className="inline-flex md:hidden" />
          DIED
        </h1>
        <a
          onClick={handleRespawnClick} // For development, remove later.
          href="#you-died-go-home"
          id="respawnText"
          className="absolute inset-0 flex items-center justify-center text-2xl text-neutral-400 opacity-0 transition-colors duration-300 hover:text-red-500 md:text-3xl"
        >
          Click here to respawn ↻
        </a>
        <p
          id="reasonOfDeath"
          className="absolute inset-0 mt-6 flex items-center justify-center text-neutral-500/80 opacity-0 md:mt-8"
        >
          - 404: Death by broken link! -
        </p>
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Here I have the scope/ref at the container div. It is always better to have container divs for the animations instead of the entire screen. Remember to change the anchor link to anywhere you want and don't forget to turn it to a next/link if you are using NextJs :)

For now, that's all there is. Just a concept with a nice and easy way of making animations with framer-motion. Preview it here, enjoy and have a good day!

Top comments (0)