---
title: "Framer Horizontal Scroll & Scroll Effects (With Code)"
description: "How to build horizontal scroll sections and scroll-linked effects in Framer, from the native Scroll component to code overrides for scroll-driven horizontal panning."
canonical_url: "https://framerhub.io/blog/framer-horizontal-scroll-guide"
last_updated: "2026-09-18T00:00:00.000Z"
---

**Horizontal scroll** shows up constantly on modern portfolio and agency sites: a row of projects you swipe or scroll through sideways, a "scroll-jacked" section where vertical scrolling drives horizontal movement. Framer supports the simple version natively and the more advanced scroll-jacking pattern with a bit of code.

This guide covers both: the native Scroll component for straightforward horizontal content, and code overrides for scroll-linked horizontal panning and parallax effects.

---

## Native horizontal scroll with Framer's Scroll component

Framer's Scroll component supports a horizontal direction out of the box. Drag it onto the canvas, set its direction to horizontal, and place content inside it in a row. Visitors can scroll sideways with a trackpad swipe, mouse wheel, or touch drag.

**Setting it up:**

1. Add a Scroll component to your canvas.
2. Set its direction to Horizontal in the properties panel.
3. Place your content (cards, images, project tiles) inside, arranged left to right.
4. Set the Scroll component's width to your viewport width and let content overflow horizontally within it.
5. Preview and test the scroll/swipe interaction on both trackpad and touch.

This covers the most common use case: a horizontally-scrolling row of cards or images, contained within its own section, that doesn't affect the rest of the page's normal vertical scroll behavior.

---

## The scroll-jacking pattern: vertical scroll drives horizontal movement

The more advanced pattern, seen on many agency and portfolio sites, pins a section in place while normal vertical page scrolling drives horizontal movement of its content. This isn't something the native Scroll component does; it needs a code override that reads vertical scroll progress and maps it to a horizontal transform.

```tsx
import type { ComponentType } from "react"
import { useRef } from "react"
import { useScroll, useTransform, motion } from "framer-motion"

export function withScrollJackHorizontal(Component): ComponentType {
    return (props) => {
        const containerRef = useRef(null)
        const { scrollYProgress } = useScroll({
            target: containerRef,
            offset: ["start start", "end end"]
        })

        const x = useTransform(scrollYProgress, [0, 1], ["0%", "-75%"])

        return (
            <div ref={containerRef} style={{ height: "300vh", position: "relative" }}>
                <div style={{ position: "sticky", top: 0, height: "100vh", overflow: "hidden" }}>
                    <motion.div style={{ x, display: "flex", height: "100%" }}>
                        <Component {...props} />
                    </motion.div>
                </div>
            </div>
        )
    }
}
```

**How this works:** the outer container is set to a tall height (`300vh` here) to give enough vertical scroll distance to drive the effect. The inner content is pinned with `position: sticky` while its horizontal position (`x`) is driven by how far the visitor has scrolled through that tall container. Adjust the container height to control how much vertical scrolling maps to the full horizontal traversal, and adjust the `-75%` end value based on how far your content needs to travel.

---

## Adding a parallax effect to a horizontal scroll section

For extra depth, different elements within a horizontal scroll section can move at different speeds relative to scroll progress, a classic parallax effect adapted to the horizontal axis.

```tsx
import type { ComponentType } from "react"
import { useScroll, useTransform, motion } from "framer-motion"

export function withHorizontalParallaxLayer(speed: number): (Component) => ComponentType {
    return (Component) => (props) => {
        const { scrollYProgress } = useScroll()
        const x = useTransform(scrollYProgress, [0, 1], ["0%", `${-100 * speed}%`])

        return <motion.div style={{ x }}><Component {...props} /></motion.div>
    }
}
```

Apply `withHorizontalParallaxLayer(0.5)` to a background layer and `withHorizontalParallaxLayer(1)` to your foreground content. The background moves at half the speed of the foreground, creating a sense of depth as the visitor scrolls.

---

## Progress indicators for horizontal scroll sections

Visitors can lose track of how much horizontal content remains, especially in a scroll-jacked section that doesn't look like a typical page. A simple progress bar tied to the same scroll value helps:

```tsx
import type { ComponentType } from "react"
import { useScroll, motion } from "framer-motion"

export function withScrollProgressBar(Component): ComponentType {
    return (props) => {
        const { scrollYProgress } = useScroll()

        return (
            <>
                <Component {...props} />
                <motion.div
                    style={{
                        scaleX: scrollYProgress,
                        position: "fixed",
                        bottom: 0,
                        left: 0,
                        right: 0,
                        height: 4,
                        backgroundColor: "#111",
                        transformOrigin: "left"
                    }}
                />
            </>
        )
    }
}
```

This renders a thin bar that fills left to right as the visitor scrolls through the section, giving a clear visual cue of progress through the horizontal content.

---

## Design and UX considerations

**Signal that content continues sideways.** A partially visible card cut off at the edge of the viewport is a stronger visual cue than a fully visible last card, since it signals there's more to see.

**Don't scroll-jack more content than necessary.** Long scroll-jacked sections can feel disorienting, especially if a visitor tries to scroll past quickly and instead gets stuck moving slowly through pinned horizontal content. Keep these sections focused, a handful of items, not an entire long list.

**Provide a non-scroll way to navigate on desktop.** Arrow buttons or visible pagination dots give visitors a clear, discoverable way to move through content beyond scrolling or dragging, useful for accessibility and for visitors who don't realize the section is interactive.

**Test touch behavior carefully.** On mobile, a horizontal scroll section inside a vertically scrolling page can create gesture conflicts. Test that swiping sideways doesn't accidentally trigger vertical page scroll, and vice versa.

---

## Performance considerations

Scroll-linked animations run on every scroll event, so a few practical rules help keep things smooth:

- **Animate transform (x, y) rather than left/right**, since transform-based animation stays on the compositor and avoids layout recalculation.
- **Avoid stacking too many independent scroll-linked layers** in one section. Each one adds computation on every scroll frame; a handful of parallax layers is reasonable, dozens is not.
- **Test on a mid-range mobile device**, not just a desktop browser, since scroll-linked effects are more likely to visibly stutter on lower-powered hardware.

---

## Combining horizontal scroll with CMS content

Horizontal scroll sections are often more useful when driven by real, repeating CMS content, a project gallery, product carousel, or testimonial row, rather than a fixed number of hardcoded cards.

Connect a Collection List to your horizontal Scroll component (native) or map CMS items into your custom scroll-jacked container (code override), so adding a new project, product, or testimonial to your CMS collection automatically extends the horizontal section without touching layout code. This is the same principle behind CMS-driven design generally: separate the content from the presentation so updates don't require rebuilding structure.

For more on structuring CMS collections that feed into layouts like this, see the [Framer CMS complete guide](/blog/framer-cms-complete-guide).

---

## Browser and gesture compatibility

**Trackpad vs. mouse wheel behavior differs.** Trackpads naturally support horizontal swipe gestures; traditional mouse wheels typically only generate vertical scroll events. Test your horizontal scroll section with both input types, and consider whether you need to translate vertical wheel input into horizontal movement for mouse users specifically.

**Touch devices need generous hit areas.** Interactive elements within a horizontally-scrolling row (buttons, links) need adequate spacing so a swipe gesture doesn't accidentally trigger a tap on a neighboring element.

**Reduced motion preferences apply here too.** For scroll-jacked sections specifically, consider a simpler fallback, a standard horizontal Scroll component rather than the pinned scroll-jacking pattern, for visitors with `prefers-reduced-motion` enabled, since scroll-jacking is a more intense motion pattern than a typical hover effect.

---

## Common horizontal scroll mistakes

**Making the entire page horizontal-scroll-driven.** This pattern works best contained to a specific section, not applied to an entire site's primary navigation and content flow, which usually confuses visitors more than it impresses them.

**No indication of how much content remains.** Especially in scroll-jacked sections, visitors can feel stuck without a progress bar, dot indicators, or a partially-visible next item signaling there's more ahead.

**Ignoring keyboard and screen reader access.** A horizontal scroll section that only responds to mouse drag or touch swipe locks out keyboard-only and assistive-technology users entirely. Provide arrow key support or visible navigation controls as an alternative interaction path.

**Overly long scroll-jacked distances.** If a visitor has to scroll an unusually long distance vertically just to see a handful of horizontally-moving items, the pacing feels wrong. Tune your container height so the scroll distance matches the amount of content, not an arbitrary large number.

---

## When to use pre-built components instead

Building scroll-jacking and parallax effects from scratch is real, non-trivial work, offset calculations, sticky positioning, transform mapping, that's easy to get subtly wrong (a section that pins slightly too early, or content that doesn't quite reach the end of its scroll range). If you'd rather not build and maintain these overrides yourself, [FramerHub Components](/plugins/framer-components) includes pre-built scroll and carousel components with this kind of interaction already configured and tested, restylable from Framer's native property panel. Browse the [full component library](/components) to see what's available before building a custom scroll-jacking override from scratch.

---

## FAQ

### Does Framer have a native horizontal scroll component?

Yes. Framer's Scroll component supports a horizontal direction setting, letting you place content in a row that scrolls sideways with either a swipe/drag gesture or a mouse wheel, without writing code.

### How do I make the page scroll vertically but content move horizontally in Framer?

This "scroll-jacking" pattern, where normal vertical page scroll drives horizontal movement of a pinned section, requires a code override using scroll progress (via `useScroll`) mapped to a horizontal transform (via `useTransform`), since it's not something Framer's native Scroll component does on its own.

### Is horizontal scroll good for mobile?

It can work well on mobile since swipe gestures feel natural for horizontal browsing, but it needs deliberate design: clear visual cues that content continues sideways, adequate touch targets, and testing that swipe doesn't conflict with the page's normal vertical scroll.

### What are good use cases for horizontal scroll in Framer?

Project galleries, product carousels, testimonial rows, and timeline or process sections work well with horizontal scroll, since they present a sequence of similar items where horizontal browsing feels natural. It works less well for primary navigation or content visitors need to read linearly.

### Does horizontal scroll hurt SEO or accessibility?

It can if implemented poorly. Make sure horizontally-scrolled content is still present in the page's DOM and readable by screen readers and search engines, not rendered only after a scroll-triggered JavaScript event with no fallback. Provide keyboard navigation as an alternative to drag/swipe for accessibility.

---

## Debugging a horizontal scroll section that isn't working

A few specific symptoms come up often enough to call out directly.

**The section pins but never releases.** This usually means the outer container's height isn't tall enough relative to how far the content needs to travel, or the `end` value in your `useScroll` offset configuration doesn't match where you actually want the pin to release. Increase the container height incrementally and re-test rather than guessing at a large value upfront.

**Content jumps instead of sliding smoothly.** Check that you're animating `x` through `useTransform` on a continuous scroll progress value, not snapping between fixed positions with discrete breakpoints. A continuous transform is what produces the smooth pan; discrete jumps usually mean the transform's input/output ranges have gaps.

**The effect works on desktop but breaks on mobile.** Mobile browsers handle `position: sticky` and viewport height calculations slightly differently, especially with dynamic browser chrome (the address bar showing/hiding). Test specifically on real mobile devices, not just a resized desktop browser window, and consider a simpler fallback for smaller viewports if the effect doesn't translate well.

**Scroll feels laggy or delayed.** This is often a sign of too much computation happening on each scroll frame, multiple `useTransform` calls, unoptimized re-renders, or non-transform properties being animated alongside the intended effect. Profile with your browser's performance tools if this happens, rather than guessing at the cause.

---

## Bottom line

Framer's native Scroll component handles straightforward **horizontal scroll** sections without any code. The scroll-jacked, vertical-drives-horizontal pattern needs a code override mapping scroll progress to a transform, which is real but manageable work once you understand the sticky-container-plus-transform pattern.

If you'd rather use tested, pre-built scroll interactions than maintain custom overrides yourself, [FramerHub Components](/plugins/framer-components) covers common scroll and carousel patterns out of the box.

*Last updated: September 2026*
