Horizontal Scrolling with CSS and JavaScript: A Complete Step-by-Step Guide

- Andrés Cruz - ES En español

Horizontal Scrolling with CSS and JavaScript: A Complete Step-by-Step Guide

Many times, when designing a website, we need to stack a series of cards or containers horizontally and allow the user to scroll them from left to right. Although it is not limited to containers: you might want your entire website to have horizontal navigation, known as a horizontal site.

The web is, by nature, vertical. However, there are many real-world scenarios where stacking content horizontally and enabling side-scrolling makes complete sense: image galleries, product lists, cards, pricing plans, or even entire websites with horizontal navigation.

Horizontal scrolling with CSS is something I have implemented several times in real projects. Although it often seems trivial, the reality is that a single CSS property is not enough for it to work properly, be usable, and not break on different devices or screen sizes.

In this guide, I will show you how to properly create a horizontal scroll in CSS, when to use each technique, which errors to avoid, and how to enhance it with a touch of JavaScript when you really need it.

What is horizontal scrolling and when to use it

Horizontal scrolling is a technique that allows the user to scroll from left to right within a container, instead of the classic vertical scroll. It seems simple, but applied correctly, it can completely transform the user experience in certain contexts.

Horizontal scroll vs. vertical scroll

  • Vertical scroll: ideal for long reading and traditional navigation.
  • Horizontal scroll: perfect for repetitive visual elements, cards, or content consumed "in blocks."

It is not about replacing vertical scrolling, but about using it strategically where it adds value.

Real-world cases where using it makes sense

Some scenarios where I usually apply it:

  • Card lists
  • Carousels without JS dependencies
  • Image galleries
  • Mobile-first sections
  • Pricing plans or feature comparisons

How to create horizontal scrolling with CSS (classic method)

This is the simplest, most stable, and compatible technique. I have used it many times because it works without JavaScript and without weird hacks. Even though it seems obvious, forgetting one of the key properties is one of the most common mistakes.

Let's see how to create a div with horizontal scroll like the one in the following example:

Animation of Horizontal Scrolling with CSS

It is actually quite simple and you don't need JavaScript or any magic plugin; there are just a few steps to keep in mind. Mind you, it is not as simple as defining a single CSS rule either: you have to combine several properties correctly. The steps are:

  1. Define the height (height) of the parent container.
  2. Indicate that we do not want vertical scrolling.
  3. Enable horizontal scrolling.

Key properties: overflow-x and overflow-y

For the last two steps, a couple of CSS rules with their corresponding values are enough:

overflow-x: auto;
overflow-y: hidden;
  • overflow-x: auto enables horizontal scroll only when content overflows the container.
  • overflow-y: hidden prevents an unwanted vertical scrollbar from appearing.

The value auto is preferable to scroll because it only shows the scrollbar when really necessary; with scroll the bar always appears, even if the content fits. If you configure the CSS properly, overflow-y: hidden can end up being optional, though I usually add it as a precaution.

The parent container: size, height, and white-space

The following CSS corresponds to the parent container, which is responsible for performing the horizontal movement. Here we define its size and width all at once:

Parent container properties

width: auto;
height: 50px;
padding: 10px;
white-space: nowrap;

Basically, we set the container size and internal padding. The white-space property specifies how whitespace inside the container is handled; with the value nowrap, we indicate that there should be no line breaks, meaning all content is displayed in a single row. In my experience, this is the most important property of classic horizontal scrolling.

In addition, we define a fixed height of 50px for the container, while leaving the width as auto so it can grow as much as the content requires.

Here is the summary of the key takeaways:

  • white-space: nowrap prevents line breaks and forces content into a single row.
  • Width adapts to the content thanks to width: auto.
  • Fixed height prevents strange overflow on the vertical axis.

Child elements and why display: inline-block is important

Now we define the CSS for the container's children. The concept is simple: we are creating small boxes that must remain on the same line:

.horizontal-scroll-contenedor div {
 width: 100px;
 height: 50px;
 margin-right: 10px;
 display: inline-block;
}

Important points:

  • display: inline-block keeps all elements aligned in a single horizontal row, instead of stacking vertically as display: block would do.
  • The size of the children must not exceed the height of the parent; if it does, the content will be clipped.
  • margin-right creates the visual separation between each element.

Practical example of horizontal scroll with divs in HTML and CSS

The most important thing is that children have the same size or smaller than the parent, and that they use display: inline-block to stay on the same line. The rest of the code is contextual and you can check it out in the download links. This is how the HTML would look:

<div class="horizontal-scroll-contenedor">
	<div>Container 1</div>
	<div>Container 2</div>
	<div>Container 3</div>
	<div>Container 4</div>
	<div>Container 5</div>
	<div>Container 6</div>
	<div>Container 7</div>
	<div>Container 8</div>
	<div>Container 9</div>
	<div>Container 10</div>
	<div>Container 11</div>
	<div>Container 12</div>
	<div>Container 13</div>
	<div>Container 14</div>
	<div>Container 15</div>
	<div>Container 16</div>
	<div>Container 17</div>
	<div>Container 18</div>
	<div>Container 19</div>
	<div>Container 20</div>
</div>

Complete CSS based on what was explained:

.horizontal-scroll-contenedor {
 width: auto;
 height: 50px;
 padding: 10px;
 overflow-x: auto;
 overflow-y: hidden;
 white-space: nowrap;
}
.horizontal-scroll-contenedor div {
 width: 100px;
 height: 50px;
 margin-right: 10px;
 display: inline-block;
}

And you can see the result below:

Container 1
Container 2
Container 3
Container 4
Container 5
Container 6
Container 7
Container 8
Container 9
Container 10
Container 11
Container 12
Container 13
Container 14
Container 15
Container 16
Container 17
Container 18
Container 19
Container 20

This pattern is simple, predictable, and easy to maintain, which is why I keep using it when I don't need advanced effects.

Horizontal scroll with Flexbox (the modern option)

Nowadays, if I have to choose, Flexbox is usually my first option. It is cleaner, more semantic, and eliminates the need to use white-space and inline-block, which are properties conceptually not intended for layout design.

Why Flexbox simplifies horizontal scrolling

With Flexbox, the code becomes much more readable. The container only needs three properties:

.flex-container {
 display: flex;
 flex-wrap: nowrap;
 overflow-x: auto;
}

And for the child elements:

.flex-item {
 flex: 0 0 auto;
}
  • flex-wrap: nowrap prevents elements from breaking into a new line, equivalent to white-space: nowrap in the classic method.
  • flex: 0 0 auto maintains the natural size of each element without Flexbox stretching or shrinking it. The three values are: flex-grow: 0 (do not grow), flex-shrink: 0 (do not shrink), and flex-basis: auto (respect declared size).

When to choose Flexbox over inline-block

  • I use Flexbox when:
    • The layout is modern and already uses Flexbox elsewhere.
    • I need vertical alignment of the elements.
    • I want cleaner and more readable code.
  • I use inline-block when:
    • I am looking for maximum compatibility with legacy browsers.
    • It is a small or very simple project.
    • I do not want to introduce Flexbox if the rest of the code does not use it.

Scroll snapping: the magnet effect when scrolling

Here we enter a more advanced level. Scroll snapping is a CSS technique that causes the scroll to automatically "latch" onto the nearest element once scrolling finishes, as if it had a magnet. The result feels polished and professional, especially on mobile.

scroll-snap-type and scroll-snap-align

These two properties create the famous "magnet" effect:

.slider {
 display: flex;
 overflow-x: scroll;
 scroll-snap-type: x proximity;
}
.card {
 scroll-snap-align: center;
}
  • scroll-snap-type: x proximity activates snap on the horizontal axis with a certain tolerance; you can change proximity to mandatory if you want snapping to always be mandatory.
  • scroll-snap-align: center aligns each element to the center of the container when snapping. Other possible values are start and end.

This is ideal for:

  • Mobile-first experiences
  • Touch carousels
  • "Plans" or "Features" sections

I have used it quite a bit in responsive projects because it greatly improves the mobile experience without needing JavaScript.

Common mistakes when implementing horizontal scroll

  • The scroll does not appear
    • Missing overflow-x: auto or overflow-x: scroll.
    • The content does not actually overflow the container width.
    • The container width has a fixed value that prevents overflow.
  • Elements break into multiple lines
    • Missing white-space: nowrap (classic method).
    • Missing flex-wrap: nowrap (Flexbox method).
  • Height and overflow issues
    • Container height poorly defined or undefined.
    • Child elements taller than the parent, creating unwanted vertical scroll.
  • These mistakes are more common than they seem; I have come across them more than once in projects of various sizes.

Is JavaScript needed for horizontal scrolling?

In most cases: no. Pure CSS is:

  • Faster (no JavaScript overhead).
  • Simpler to maintain.
  • More accessible for screen readers and assistive technologies.
  • More maintainable long term.

JavaScript only makes sense when you need extra functionality: arrow navigation buttons, autoplay, programmatic scrolling, or more complex animations.

Frequently asked questions about horizontal scrolling with CSS

  • Can horizontal scroll be done with CSS alone?
    • Yes, and in most cases it is the best option. You don't need any external library.
  • Flexbox or inline-block?
    • Flexbox is more modern and clean; inline-block remains valid for simple cases.
  • Does it work on mobile?
    • Yes, especially well with touch gestures. If you add scroll-snap, the experience improves even further.
  • Does it affect SEO?
    • No, as long as content is accessible and visible to crawlers. Google seamlessly indexes content inside a div with horizontal scroll.
  • How do I hide the horizontal scrollbar with CSS?
    • You can visually hide it using the ::-webkit-scrollbar pseudo-class with display: none, although this is not standard across all browsers:
.horizontal-scroll-contenedor::-webkit-scrollbar {
 display: none;
}
/* For Firefox */
.horizontal-scroll-contenedor {
 scrollbar-width: none;
}

Extra: automatic scrolling with JavaScript using scrollLeft

Although pure CSS is more than enough for most cases, JavaScript opens the door to more dynamic capabilities: arrow navigation buttons, autoplay, or code-controlled animations. Let's see how to do it using the scrollLeft property and the scrollTo() method.

The scrollLeft property allows us to read or set the number of pixels a container is shifted horizontally. To move the scroll by 200 pixels all at once:

// Select the container
const contenedor = document.querySelector(".horizontal-scroll-contenedor");
// Assign the scroll value
contenedor.scrollLeft = 200;

In modern JavaScript, you can use the scrollTo() method with an options object so the movement is a smooth glide instead of an abrupt jump:

const contenedor = document.querySelector(".horizontal-scroll-contenedor");
contenedor.scrollTo({
 left: 200,
 behavior: 'smooth' // This makes the scroll animated
});

If you want to implement arrow navigation buttons (next/previous), the basic logic is to add or subtract a value from the container's current scrollLeft:

const contenedor = document.querySelector(".horizontal-scroll-contenedor");
const btnSiguiente = document.querySelector("#btn-siguiente");
const btnAnterior = document.querySelector("#btn-anterior");

btnSiguiente.addEventListener("click", () => {
 contenedor.scrollBy({ left: 200, behavior: "smooth" });
});

btnAnterior.addEventListener("click", () => {
 contenedor.scrollBy({ left: -200, behavior: "smooth" });
});

The scrollBy() method scrolls relative to the current position, which makes it ideal for "next" and "previous" type buttons without needing to calculate absolute positions.

Conclusion

Horizontal scrolling with CSS is not only possible, but recommended when done properly. You don't need magic libraries or heavy JavaScript; just an understanding of how overflow, display, and the browser layout model work.

After testing several solutions in real projects, my recommendation is clear:

  • Start simple with overflow-x: auto and white-space: nowrap.
  • Use Flexbox if you can; the code stays much cleaner.
  • Add scroll-snap only when it adds real value to the user experience.
  • Avoid unnecessary hacks and resort to JavaScript only for features CSS cannot cover.

Learn how to create horizontal scrolling in CSS using overflow-x, Flexbox, and scroll-snap. Includes real-world examples, common mistakes, and how to add navigation buttons with JavaScript.


Únete a la comunidad de desarrolladores que han decidido dejar de picar código y empezar a construir productos reales. Recibe mis mejores trucos de arquitectura cada semana:

I agree to receive announcements of interest about this Blog.