English

Menu

Web Development

Damien Schneider

How to make a Vercel-like navigation using ReactTS, Tailwind, and Framer Motion

Sunday, August 4, 2024

TLDR

In this tutorial, we built a Vercel-like navigation menu using ReactTS, Tailwind CSS, and Framer Motion. The key steps include:

  1. Project setup: Create a React project with TypeScript and install Tailwind CSS and Framer Motion.

  2. Component creation: Build the navigation component with React, managing hover states with React hooks.

  3. Implement animations: Use Framer Motion for smooth hover animations.

  4. Styling: Apply Tailwind CSS classes for responsive and aesthetic design.

  5. Reusability: Make the component reusable by parameterizing button data.

By following these steps, you can create a customizable and robust navigation menu with smooth animations, enhancing your web application's user experience.

The component Demo can be found at CuiCui.day

Creating a sophisticated and smooth navigation menu similar to Vercel's can significantly enhance the user experience of your web application. In this tutorial, we will walk through building a customizable and robust navigation menu using ReactTS, Tailwind CSS, and Framer Motion. Whether you're a beginner or an advanced React developer, this guide will provide you with best practices and detailed steps to achieve a seamless navigation experience.

Introduction

Navigation menus are crucial for any web application as they guide users through the content and functionalities of the site. A well-designed navigation menu can make a significant difference in user experience. In this tutorial, we'll build a navigation menu inspired by Vercel's elegant design, using ReactTS, Tailwind CSS, and Framer Motion.

How It Works

The navigation menu we'll create features buttons that change appearance when hovered over, thanks to Framer Motion's animation capabilities. Each button will have a smooth animation effect, providing a responsive and visually appealing experience. Tailwind CSS will help us style the components efficiently, while ReactTS will manage the component logic and state.

Best Practices

To ensure the navigation menu is robust, reusable, and maintainable, we'll adhere to the following best practices:

- Componentization: Break down the navigation into smaller, reusable components.

- State Management: Use React's state hooks to manage active elements and hover states.

- Responsive Design: Use Tailwind CSS to ensure the navigation menu is responsive across different screen sizes.

- Performance Optimization: Utilize Framer Motion for smooth animations without compromising performance.

Step-by-Step Guide

Setting Up the Project

First, set up a new React project with TypeScript, Tailwind CSS, and Framer Motion. If you don't have these tools installed, you can follow these steps:

1. Create a new React project with TypeScript:

npx create-react-app vercel-navigation --template typescript
cd

2. Install Tailwind CSS:

npm install -D

Configure Tailwind in `tailwind.config.js` and add the necessary styles to `src/index.css`.

3. Install Framer Motion:

npm

Creating the Navigation Component

Create a new file VercelNavigation.tsx in the src/components directory and add the following code:

"use client";
import { AnimatePresence, motion } from "framer-motion";
import React, { useState } from "react";
const dataButtons = [
  { label: "Home", href: "#" },
  { label: "About", href: "#" },
  { label: "Contact", href: "#" },
  { label: "Terms & conditions", href: "#" },
  { label: "Cuicui.day", href: "#" },
];
export function VercelNavigation() {
  const [elementFocused, setElementFocused] = useState<number | null>(null);
  const handleHoverButton = (index: number | null) => {
    setElementFocused(index);
  };
  return (
    <nav
      className="flex flex-col sm:flex-row"
      onMouseLeave={() => handleHoverButton(null)}
    >
      {dataButtons.map((button, index) => (
        <button
          type="button"
          key={button.label}
          onMouseEnter={() => handleHoverButton(index)}
          className="text-neutral-500 text-sm font-medium py-1 px-2 rounded relative whitespace-nowrap inline-flex w-fit"
        >
          {button.label}
          <AnimatePresence>
            {elementFocused === index && (
              <motion.div
                className="absolute top-0 left-0 right-0 bottom-0 bg-neutral-200 dark:bg-neutral-800 rounded-md -z-10"
                initial={{ opacity: 0, scale: 0.95 }}
                animate={{ opacity: 1, scale: 1 }}
                exit={{ opacity: 0, scale: 0.9 }}
                transition={{ duration: 0.2 }}
                layout
                layoutId="focused-element"
              />
            )}
          </AnimatePresence>
        </button>
      ))}
    </nav>
  );
}

Implementing Animations

We use Framer Motion's `AnimatePresence` and `motion.div` components to handle the animations. The `motion.div` is conditionally rendered when a button is hovered, creating a smooth scaling and opacity transition.

Styling with Tailwind CSS

Tailwind CSS classes are used to style the navigation and buttons. Here’s a breakdown of the applied styles:

- flex flex-col sm:flex-row: Makes the navigation responsive, changing from a column layout on small screens to a row layout on larger screens.

- text-neutral-500 text-sm font-medium py-1 px-2 rounded relative whitespace-nowrap inline-flex w-fit: Styles the buttons with neutral text color, padding, rounded corners, and ensures they fit their content.

Making It Reusable

To make this component reusable, parameterize the button data and styles. This allows you to easily customize the navigation for different projects.

interface ButtonData {
  label: string;
  href: string;
}
interface VercelNavigationProps {
  buttons: ButtonData[];
}
export function VercelNavigation({ buttons }: VercelNavigationProps) {
  const [elementFocused, setElementFocused] = useState<number | null>(null);
  const handleHoverButton = (index: number | null) => {
    setElementFocused(index);
  };
  return (
    <nav
      className="flex flex-col sm:flex-row"
      onMouseLeave={() => handleHoverButton(null)}
    >
      {buttons.map((button, index) => (
        <button
          type="button"
          key={button.label}
          onMouseEnter={() => handleHoverButton(index)}
          className="text-neutral-500 text-sm font-medium py-1 px-2 rounded relative whitespace-nowrap inline-flex w-fit"
        >
          {button.label}
          <AnimatePresence>
            {elementFocused === index && (
              <motion.div
                className="absolute top-0 left-0 right-0 bottom-0 bg-neutral-200 dark:bg-neutral-800 rounded-md -z-10"
                initial={{ opacity: 0, scale: 0.95 }}
                animate={{ opacity: 1, scale: 1 }}
                exit={{ opacity: 0, scale: 0.9 }}
                transition={{ duration: 0.2 }}
                layout
                layoutId="focused-element"
              />
            )}
          </AnimatePresence>
        </button>
      ))}
    </nav>
  );
}

Now, you can use this component anywhere in your application by passing the button data as props:

import React from "react";
import { VercelNavigation } from "./components/VercelNavigation";
const buttonData = [
  { label: "Home", href: "#" },
  { label: "About", href: "#" },
  { label: "Contact", href: "#" },
  { label: "Terms & conditions", href: "#" },
  { label: "Cuicui.day", href: "#" },
];
function App() {
  return (
    <div className="App">
      <VercelNavigation buttons={buttonData} />
    </div>
  );
}
export default App;

Conclusion

In this tutorial, we created a Vercel-like navigation menu using ReactTS, Tailwind CSS, and Framer Motion. We explored how to manage state, implement animations, style with Tailwind CSS, and make the component reusable. By following these steps, you can enhance your web applications with a smooth and visually appealing navigation menu. Happy coding!

Concrétisez votre projet

Pour une solution personnalisée :

Plan a meeting with Damien S.

Concrétisez votre projet

Pour une solution personnalisée :

Plan a meeting with Damien S.