How to Create a Responsive Timeline Component in React

How to Create a Responsive Timeline Component in React

With the help of CSS pseudo elements
Ferenc Almasi β€’ 2024 February 13 β€’ Read time 10 min read β€’ React v18.2
Learn how you can build a responsive timeline component in React with the help of pseudo elements in CSS.
  • twitter
  • facebook
React Previous Tutorial

A timeline component is often used to graphically represent events or activities displayed chronologically along a horizontal or vertical axis. It allows users to visualize the sequence of events over time, providing a clear understanding of the temporal relationships between different items.

In this tutorial, we'll take a look at how to create a Timeline component in React with the help of pseudo-element in CSS. By the end of this tutorial, we'll have the following responsive component ready that you can use in your projects:

Mobile vs desktop design of timeline component
Mobile vs desktop design of the Timeline component

Laying out the Component

First, let's see how we want to reference the Timeline component. It'll accept an items prop through which we can pass an array of timeline points for display:

Copied to clipboard! Playground
import { Timeline } from './Timeline'

export const App = () => {
    const data = [
        {
            heading: 'Eat',
            content: 'Everything you find in the fridge',
        },
        { ... },
        { ... }
    ]

    return <Timeline items={data} />
}
App.jsx
Calling the Timeline component
  • Lines 4-11: Each item can have a heading and a content prop that will be displayed by the component. Here we can create as many elements as necessary. In a real-world scenario, this data would be retrieved from a backend service, such as a CMS.
  • Line 13: The data variable is passed to the items prop on the Timeline component. This is the only prop we're going to need to build this component.
πŸ” Login to get access to the full source code in one piece. TypeScript types are included.

Building the Timeline Component

Now that we know how the timeline will function, let's take a look at the component itself. If you haven't already, create a new file called Timeline.jsx in your project and add the following code inside the file:

Copied to clipboard! Playground
import classNames from 'classnames'
import './timeline.css'

export const Timeline = ({ items }) => (
    <ul className="timeline">
        {items?.map((point, index) => {
            const { heading, content } = point
            const first = index === 0
            const last = items.length === index + 1
            const even = (index + 1) % 2 === 0

            return (
                <li className={classNames(
                    'item',
                    even && 'even',
                    (first || last) && 'overlay',
                    last && 'last'
                )}>
                    <strong>{heading}</strong>
                    <span>{content}</span>
                </li>
            )
        })}
    </ul>
)

Timeline.jsx
Create the template for the Timeline component
  • Line 1: In this component, we make use of the popular classNames library that can be used to conditionally generate class names in React.
  • Line 2: Most of the component is made up of CSS. Make sure you import a dedicated CSS file where we organize the styles associated with the component.
  • Lines 7-10: For improved accessibility, the element is displayed as an unordered list. We can loop through the items using a map to display the elements. Inside the map, we need to mark the first, last, and even items, which we can do using indexes.
    • first: The first item can be determined by checking if the index is 0, meaning we're in the first iteration of the loop.
    • last: The last item can be determined by checking if the index matches the length of the array.
    • even: Even items can be determined using the modulo operator. This returns the remainder of the equation when the operand (index + 1) is divided by the second operand (2). If this equation returns 0, it means there's no remainder, so iterations like 2, 4, and 6 will be marked as even.
  • Lines 13-18: Using the classNames library, we can conditionally apply class names to the li based on the aforementioned variables. We'll use these classes to apply different styles. The elements will always have an .item class, and they'll conditionally get an .even, .overlay, and .last classes when the variables are evaluated to true.
  • Lines 19-20: Inside the li, we can display the heading and content properties of each item that is passed to the component through the items prop.
Looking to improve your skills? Check out our interactive course to master React from start to finish.
Master Reactinfo Remove ads

Styling the Timeline

Now that we have the necessary DOM elements for the timeline, let's see the styles we need for the mobile version. Inside the CSS file of the component, append the following:

Copied to clipboard! Playground
:root {
    --timeline-padding: 32px;
}

.timeline {
    display: flex; 
    position: relative; 
    flex-direction: column; 
    gap: 1rem; 
}

.timeline strong {
    font-size: 21px;
}

.item {
    display: flex;
    position: relative;
    padding: 1.5rem; 
    flex-direction: column;
    gap: 1rem;
    border-radius: 0.5rem;
    background: #222431;
    background: linear-gradient(to bottom right, #222431, #233d44);
    color: #FFF;
}
timeline.css
Add styles for the Timeline component

When writing CSS, always follow a mobile-first approach to end up with less CSS.

  • :root: First, we need to create a CSS variable on the :root element. Depending on the padding of the timeline, this value could be different. In our case, we need to work with 32px to correctly align elements later on.
  • .timeline: The ul itself is displayed as a flex element, with a flex-direction set to column, so that each li is displayed under each other. The whitespace between elements is added with a 1rem gap. We'll also need to set its position to relative, as we'll absolutely position the vertical line between the elements.
  • .timeline strong: To increase the weight of the heading, we can increase the font size. Depending on your use case of the Timeline component, this element could also be replaced with an h2 to use headings for SEO.
  • .item: The items themselves are also displayed as flex for easier alignment of child elements, with the same gap, small padding, and border-radius. We also use a linear gradient for the background. This one also needs to have its position set to relative, as we'll absolutely position the dots to these elements.

On small screen devices, we don't have enough space to display the classical timeline alignment of elements without distorting them, so we simply display them as a list of items. The above CSS rules will create the following layout:

  • Eat Everything you find in the fridge
  • Code New apps
πŸ” Login to get access to the full source code in one piece. TypeScript types are included.

Adding Pseudo Elements

With these CSS rules present, we have the base styles for the element. There's only one thing left to do: add styles for the desktop version. First, let's add the proper alignment for the items. Extend the CSS with the following media query:

Copied to clipboard! Playground
@media (min-width: 500px) {
    .item {
        width: calc(50% - var(--timeline-padding));
    }

    .even {
        align-self: flex-end; 
    }
}
timeline.css
Add alignment for larger screens

This will ensure that each item will only take up half the width of the Timeline component. For every .even element, we'll display them on the right side using align-self: flex-end. With these rules in place, we'll have the following layout:

  • /timeline.css timeline.css
  • /Timeline.jsx Timeline.jsx
  • /App.jsx App.jsx
Refresh

To add a vertical line in the middle, we can extend the CSS with an ::after pseudo-element on the .timeline. Append the following CSS in the editor above to create a vertical line between the elements:

Copied to clipboard! Playground
.timeline::after {
    content: '';
    position: absolute;
    width: 1px;
    background: #222431;
    left: 50%;
    height: 100%;
    z-index: -1;
}
timeline.css
Add the vertical line to the Timeline component

This works by absolutely positioning the element with a 1px width and 100% height to the center of the .timeline using left: 50%. Note that we've also set its z-index to -1. This is because we want this element to appear behind the dots that we'll add next. To create the dots, we need to target the ::before pseudo-element of the .item elements. Extend the CSS with the following rules to add the dots:

Copied to clipboard! Playground
.item::before {
    content: '';
    position: absolute;
    background: #222431;
    width: 1rem;
    height: 1rem;
    border-radius: 100%;
    top: 50%;
    right: calc(var(--timeline-padding) * 2 * -1);
    transform: translate(-50%, -50%);
    border: 8px solid #FFF;
}

.even::before {
    content: '';
    left: calc(var(--timeline-padding) * -1);
    right: auto;
}
timeline.css
Extend the CSS to add the dots to the timeline

This will create the dots next to each element. To horizontally center the dots with each .item, we can use top: 50%, and translate(-50%, -50%). We also use an 8px border with the same color as the background to create the illusion of breaking up the vertical line. For .even elements, we need to switch the positioning from right to left, using the same CSS variable.

To create negative values with CSS variables, we have to multiply the value by -1

The alignment of timeline elements
The alignment of dots relative to each item

There's one last thing missing from our CSS. Currently, the vertical line extends above and below the first and last elements. We can remove these by adding overlay elements using another pair of pseudo-elements. To finish the component, we need to extend it with these last sets of CSS:

Copied to clipboard! Playground
.overlay::after {
    content: '';
    position: absolute;
    top: 0;
    right: -40px;
    background: #FFF;
    width: 1rem;
    height: calc(50% - 8px);
}

.last::after {
    top: auto;
    bottom: 0;
}

.last.even::after {
    left: -40px;
}
timeline.css
Removing vertical line above and below first and last items

These elements have the same background as the page. We need a 50% height for them from the top (for the first .overlay) and bottom (for the .last element). Note that we need to change the right position to left for .even elements, to ensure the overlay is correctly positioned, regardless of whether we have an even (e.g. 4) or odd (e.g. 3) number of elements.


Summary

Congratulations! You've just completed the creation of your very first Timeline component in React! Interested in elevating this project even further? Here are some ideas on how to enhance the functionality of this component:

  • Allow the use of HTML for the content.
  • Enable configuration of styles to make it reusable with different variants.
  • Implement virtualized loading for large lists.

Are you looking for more React projects? Check out our React projects roadmap, a comprehensive guide that offers inspiration on what to build next and how to approach it. Thank you for reading, and happy coding!

React Projects Roadmap
  • twitter
  • facebook
React
Did you find this page helpful?
πŸ“š More Webtips
Mentoring

Rocket Launch Your Career

Speed up your learning progress with our mentorship program. Join as a mentee to unlock the full potential of Webtips and get a personalized learning experience by experts to master the following frontend technologies:

Courses

Recommended

This site uses cookies We use cookies to understand visitors and create a better experience for you. By clicking on "Accept", you accept its use. To find out more, please see our privacy policy.