• CTRL ALT News
  • Posts
  • Build a Responsive Landing Page with Auto-Scrolling Marquees in React and Tailwind CSS (Beginner Friendly) | [Source Code Included]

Build a Responsive Landing Page with Auto-Scrolling Marquees in React and Tailwind CSS (Beginner Friendly) | [Source Code Included]

In this article, I am going to show you how to create a responsive landing page with auto-scrolling marquees in React and Tailwind CSS. You will learn how to use React state and a third-party library for handling the marquees.

Build a Responsive Landing Page with Auto-Scrolling Marquees in React and Tailwind CSS (Beginner Friendly) | [Source Code Included]

Get the source code here.

In this article, I am going to show you how to create a responsive landing page with auto-scrolling marquees in React and Tailwind CSS. You will learn how to use React state and a third-party library for handling the marquees.

Watch the Youtube version:

Setting Up the Project

The very first thing we need to do is set up our react project. Simply start off by opening any console of your choice. After opening the console type the following command:

> npm create vite@latest my-react-app --template react-ts

You will then be asked to select a few options for the project, you need to choose the following:

√ Select a framework: » React
√ Select a variant: » TypeScript

Your React project is now set up, but don’t close the console just yet.

TailwindCSS

This part can also be found in the original TailwindCSS docs, if you prefer it can be read here.

TailwindCSS is not set up by default when creating our project like this, so we need to do this ourselves. The first thing we need to do is CD’ing our console into our new React folder. If you haven’t already closed your console, you can type the following command:

> cd my-react-app

If you did close the console, simply open it up inside the React app.

Now we need to install the packages needed for TailwindCSS, simply type the following command:

> npm install -D tailwindcss postcss autoprefixer
> npx tailwindcss init -p

You should now have all the packages needed and a “tailwind.config.js” file in your React app.

After that you need to configure the template paths, this can be done inside your “tailwind.config.js” file. It has to look like this:

/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

The last thing we need to do is add our TailwindCSS directives to our CSS. Simply open your “index.css” file, and make sure it looks like this:

@tailwind base;
@tailwind components;
@tailwind utilities;

TailwindCSS is not set up!

Images

We need a few images for this project to work. I recommend you find about 10 images you would like to use. The images you want to use should be put inside your “public” folder. I named my images: image (1), image (2), image (3), and so on.

Cleaning Up the Files

Go into your “app.tsx” file and make sure everything is removed except the App function itself, the entire file should look like this:

function App() {
  return (
    
  )
}

export default App

Everything is now set up for coding the application.

Coding

We need to import a few things before starting, put the following two lines of code at the top of your “App.tsx” file:

import { useEffect, useState } from "react";
import Marquee from "react-fast-marquee";

Marquee Details

The very first thing we need to create is an array of objects containing the images for our marquees. For this, we also need a type definition:

type MarqueeDetail = {
  image: string;
  id: number;
}

const marqueeDetails: MarqueeDetail[] = [
  {
    image: "/image (1).webp",
    id: 1,
  },
  {
    image: "/image (2).webp",
    id: 2
  },
  {
    image: "/image (3).webp",
    id: 3
  },
  {
    image: "/image (4).webp",
    id: 4
  },
  {
    image: "/image (5).webp",
    id: 5
  },
  {
    image: "/image (6).webp",
    id: 6
  },
  {
    image: "/image (7).webp",
    id: 7
  },
  {
    image: "/image (8).webp",
    id: 8
  },
]

As you can see we are creating a type for an object containing the route to the image and an ID for the image. I named my images: image (1), image (2), image (3), and so on, but you can change the image paths to whatever you named it. The code should be put outside the App function.

SplitArray Function

Our app automatically calculates how many marquees will be needed to fit the height of the screen. Therefore, we need to split the images into different arrays for each marquee, so each marquee has its images. This is the function:

function splitArray<T>(array: Array<T>, parts: number): Array<Array<T>> {
  const size = Math.ceil(array.length / parts);
  return Array.from({ length: parts }, (v, i) =>
    array.slice(i * size, i * size + size)
  );
}

This function should also be put outside the App function.

Card Component

We will create a component for each of the image cards used inside the marquees, this is the Card component code:

function Card(props: { marquee: MarqueeDetail }) {
  return (
    <div className="w-[24rem] min-h-[16rem] h-[16rem] rounded-3xl bg-gray-300  mx-8 flex items-center justify-center ">
      <img src={props.marquee.image} className="relative h-full w-full object-cover rounded-3xl select-none	" alt={"image"} />
    </div>
  )
}

This component should, of course, be placed outside the App component.

State and useEffect

Inside our App component, we need a single state and some code that runs inside a useEffect hook. This is the code:

const [allMarqueesDetails, setAllMarqueesDetails] = useState<Array<Array<MarqueeDetail>>>()


useEffect(() => {
    const { innerHeight: screenHeight } = window;
    const heightOfMarquee = 256; //In px
    const gapOfFlexLayout = 32; // In px
    const totalMarqueeHeight = heightOfMarquee + gapOfFlexLayout;
    const totalMarqueesAmount = Math.ceil(screenHeight / totalMarqueeHeight);
    const splitMarqueesDetails = splitArray(marqueeDetails, totalMarqueesAmount)
    setAllMarqueesDetails(splitMarqueesDetails)
  }, [])

So what does this code do? First, we define a state. The state holds an array with subarrays of MarqueeDetails.

Inside our useEffect we calculate the amount of needed marquees depending on the height of the screen. The heightOfMarquee and gapOfFlexLayout variables are defined from the height of our elements, which is controlled by our CSS. If you decide to change the CSS around, you might need to change these variables. The number of marquees needed is passed to our splitArray function to split the images.

Left Section

Let’s start creating the layout, we will start with the left side of the page. This part of the page contains the navbar, title, subheader, and signup form. Here is the code:

<div className="h-screen w-screen flex">
    <section className="w-full sm:w-1/2 lg:w-3/5 h-full flex flex-col">
        <nav className="w-full h-12 border border-b px-2 lg:px-16 flex items-center justify-between">
          <div className="flex font-bold">
            <p>Pixion</p>
          </div>
          <ul className="flex gap-8">
            <li><a className="border-b-2 border-b-transparent hover:border-b-blue-500 transition-colors p-1" href="#">Home</a></li>
            <li><a className="border-b-2 border-b-transparent hover:border-b-blue-500 transition-colors p-1" href="#">Pricing</a></li>
            <li><a className="border-b-2 border-b-transparent hover:border-b-blue-500 transition-colors p-1" href="#">Contact</a></li>
          </ul>
        </nav>

        <main className="w-full flex flex-col lg:flex-row flex-grow py-8 px-16 gap-16 lg:justify-center items-center h-full overflow-y-auto">
          <div className="w-full items-center text-center lg:text-left lg:items-start lg:w-1/2 h-fit flex flex-col gap-4 lg:gap-8">
            <h1 className="text-4xl sm:text-4xl md:text-6xl 2xl:text-8xl font-black">Pixion</h1>
            <h2 className="text-base  xl:text-xl">Pixion makes it easy to upload, categorize, and access all your most precious photos and videos. Relive your favorite moments.</h2>
          </div>
          <hr className="w-full lg:hidden"/>
          <div className="flex flex-col w-full lg:w-1/2 h-fit items-center justify-center gap-8">
            <h3 className="text-xl lg:text-2xl 2xl:text-3xl font-bold text-center">Sign Up Today</h3>
            <div className="w-full h-fit flex flex-col justify-center gap-4">
              <div className="flex flex-col gap-1">
                <label htmlFor="emailInput" className="text-sm">Email</label>
                <input type="email" className="w-full border p-2  px-4 rounded" id="emailInput" />
              </div>
              <div className="flex flex-col gap-1">
                <label htmlFor="passwordInput" className="text-sm">Password</label>
                <input type="password" className="w-full border p-2  px-4 rounded" id="passwordInput" />
              </div>
              <div className="flex flex-col gap-1">
                <label htmlFor="confirmPasswordInput" className="text-sm">Confirm Password</label>
                <input type="password" className="w-full border p-2 px-4 rounded" id="confirmPasswordInput" />
              </div>
            <button className="w-full p-2 bg-blue-500 rounded text-white hover:bg-blue-700 transition-colors">Sign Up</button>
            </div>
            <div className="flex w-full h-fit items-center justify-between flex-col xl:flex-row">
              <p className="text-sm text-blue-700">✓ Lifetime Cloud Storage</p>
              <p className="text-sm text-blue-700">✓ Privacy Controls</p>
              <p className="text-sm text-blue-700">✓ Share Memories</p>
            </div>
          </div>
        </main>

      </section>
</div>

This code will be put inside our App component return statement.

Right Section (The Marquees)

Now let’s get onto the marquees, this is the code:

<section className="hidden sm:flex w-1/2 lg:w-2/5 h-full border border-l flex-col justify-center gap-16 overflow-y-hidden flex-nowrap">
        {allMarqueesDetails?.map((marquees, i) => {
          return (
            <Marquee className="min-h-[16rem] w-full " autoFill direction={i % 2 == 0 ? "left" : "right"} speed={10}>
              {marquees.map((marquee) => {
                return (
                  <Card key={marquee.id} marquee={marquee} />
                )
              })}
            </Marquee>
          )
        })}
</section>

This code will be put right after our left section.

Rounding Up

You are now done and can view your website. Simply type “npm run dev” in your console, and the URL for visiting the page should pop up.