Elementor Full Screen Horizontal Scroll Containers

Elementor Full Screen Horizontal Scroll Containers

Table of Contents

In this tutorial you are going to learn how to create horizontally scrolling Elementor containers  without any plugin.

This tutorial uses the new containers element, as they are perfect for this.

See the demos here.

Straight code version (desktop and mobile ready):


GSAP version (desktop only)


GSAP with snapping version (desktop only)


  • Make any content at all horizontally scrollable
  • Works on desktop and mobile
  • Performant animation
  • No JS dependencies version
  • GSAP version
  • Snapping enabled option
  • RTL compatible

Important note: You will need the feature "Container" to be enabled under Elementor > Experiments > Container. This uses the new Container element.

Template included: The "Straight code" demo above is included as a template JSON file. The GSAP versions are not included as template files, but easy to replicate by following the tutorial.

Let's get started!

First, add an Elementor container to your page

This is a premium tutorial. Purchase access to unlock the full tutorial and download the template.

Access tutorial

  • Gain Access to This TutorialiUnlock complete access to the current tutorial: Elementor Full Screen Horizontal Scroll Containers
  • Future UpdatesiYou will get access to all future updates to this tutorial.
  • Enjoy Unlimited UsageiUse on as many of your own sites or your clients sites as you wish.

    Note that reselling or redistributing is not permitted.
$29* Purchase access

Access everything

  • Unlock every premium tutorial on Element.howiGet access to the entire library of premium tutorials on Element.how
    Preview premium tutorials
  • Get access to the CSS course for Elementor usersiAccess the complete 14 HTML chapters, 30 CSS chapters and 7 Elementor Projects.Learn more
  • Simple CSS Grid For ElementoriAn Elementor Addon to Create Awesome Grid Layouts in a Single Click for Containers, Galleries and Loop Grid. Learn more
  • free extra: ShapeDividers.com Premium AccessiLifetime Premium Access to ShapeDividers.comVisit ShapeDividers.com
  • 30 day money backiNo questions asked money back. Not what you expected? Get a refund.
  • One-time payment of only $299iNo hidden fees or subscriptions.

    Sales taxes added where applicable.
  • Limited supportiGet help when you need it. Support includes getting things working as intended.

    Support excludes customization work.
  • Lifetime access to everything Element.howiThe price reflects what is currently available on Element.how. All future updates are included, but none are promised. You pay for what is available now, and the rest is a sweet extra.

    I will say that it is definitely my intention to keep adding tutorials to Element.how.
  • Launch offer discounts!iThis all inclusive package is freshly released and to celebrate, there is a special discount available:

    10% for new customers with coupon code LAUNCH10

    20% for existing customers with coupon code LAUNCH20

    30% for CSS Course customers with coupon code LAUNCH30_CSS_STUDENT

    Available for a limited time only.
$299* Purchase All Access
* All prices are USD. Applicable taxes will be charged at checkout. Have a question? email me

Element.how also provides premium tutorials showing awesome advanced designs, check them out here.

Looking for something else? Search across 2445 Elements right here:

Checkout the Elementor Addon Finder directly


48 Responses

    1. Hey Paul!

      This is a bit complex, and above the scope of the tutorial. The tutorial is made to work with containers that are 100vw by 100vh, so that it's relatively simple and straightforward to use in the editor.

      Changing this, while possible, makes it wonky in the editor, so that's why I don't officially support it.

      However for adventurous users, both code snippets actually support this (varying containers sizes) out of the box.

      Here is how you can play with this:

      On the containers that are direct child of the horizontalScroll_translateContainer, in their advanced > custom CSS field, add this CSS, and adjust to your liking:

      selector {
      width: 70vw;
      max-width:70vw !important;
      min-width:70vw !important;

      In the editor, it will be easier to edit things if you use values that are under 100vw, instead of above. In any case, both should work.

      Different containers can have different widths.

      Hope this helps!

  1. Hi Maxime,

    in that layout (gsap version) would be possible to make a background or a container fixed/sticky in order to have an overlap effect playing with z-index?

    1. Hey,

      Not really no... not as it is at least. With extra work, it would be possible, however it is above the intended scope of this tutorial. It becomes quite specific and would rather be custom work.

      1. No problem Maxime, I've found that is possible to use fixed or absolute positioning placing that items in "horizontalScroll_innerWrapper".
        Even elementor motion effects works with GSAP setting the effect relative to "Entire Page".

      2. Alright I'm glad you found a solution that works for your use case!

        Thanks for sharing!

    1. Hey Sandra!

      Try importing my template in a new page, and then swapping all the code in the HTML element with the GSAP code.

      Let me know if that works properly.

    1. Sorry Mirko, this isn't possible.

      I had a good look at this and the problem with locomotive scroll is that it is very incompatible with Elementor. It breaks nearly every scroll related features of Elementor, such as entrance animations, scrolling effects, anchors smooth scroll, etc.

      To prevent adding smooth scroll, but creating a host of problems, I opted to go with stability instead.

    1. It's possible, however due to the all the DIVs generated, it's not a really good idea. That's why this tutorial is only about how to do it with the containers.

    1. Hey Louise!

      The problem is that your containers, for some reason, aren't display:flex; but they are display:block;

      It's the clear what the reason is... I suspect maybe your optimization plugin. It's as if some CSS is missing.


      .e-container, .e-con { display: flex;}

      should fix the problem


      1. That sorted it! Thank you 🙂 Was my caching plugin as well I suspect - minifying CSS maybe, taken that off

    1. Hey Nicholas!

      The "Shop now" button just has a "#" link that scrolls back up to the top of the page.

      Unfortunately with this design it's not easily possible to have a button to scroll to a specific scroll section. With extra JS this might be possible but it's out of the intended scope of the tutorial....


  2. I got this working on my container with 4 inner containers using the straight code version (works great) but it completely hoses my elementor nav menu. Not a 3rd party elementor addon menu widget but the native elementor menu widget.

    Without your code active, scrolling the single page website will add the class 'active' to the menu item that is in view and remove it when that section scroll out of view (just as you'd expect). With your code active, the first menu item remains active no matter where I scroll to, and the anchor links themselves no longer function properly/take the user to the correct section, or it takes the user nowhere at all. When I deactivate your code (save elementor custom code as draft) the nav menu works fine again. https://kwicks.flywheelsites.com/
    Any help?

    No caching plugins, no optimization, no minification, brand new Hello theme install. No elementor add on plugins. Thanks

  3. Hi, I've been using the horizontal scroll for some time and it worked great until the last version of Elementor.

    Since version 3.8.0 it doesn't work at all, is there an update coming soon to fix this?

    1. Hey Mounir!

      Indeed there has been class names changes with 3.8 that breaks the code slightly. Please replace "e-container" with "e-con" in these two lines of code:

      let scrollSectionsAmount = parentOfStickySection.querySelectorAll('.horizontalScroll_translateContainer > .e-container').length;
      .horizontalScroll_innerWrapper>.horizontalScroll_translateContainer>.e-container {

      To work with Elementor 3.8 and above, they should be:

      let scrollSectionsAmount = parentOfStickySection.querySelectorAll('.horizontalScroll_translateContainer > .e-con').length;
      .horizontalScroll_innerWrapper>.horizontalScroll_translateContainer>.e-con {

      You can simply copy paste the code from the tutorial, it's updated to work with 3.8. Writing here the changes needed for reference.

      Also note that the "horizontalScroll_parent", "horizontalScroll_innerWrapper" and "horizontalScroll_translateContainer" containers needs to be set to "full width" layout, and not boxed.

      1. Hi Maxime,

        Thanks a lot for your answer. I'm going to put it in place right away.

  4. Hi Maxime,

    I've updated by myself the code when elementor 3.8 went out and replaced "e-container" with "e-con" in the JS script and all is working fine but if I copy/paste all the new code It doesn't work at all.
    There are other changing in that file:


    let options = {
    x: () => -(translateXSection.scrollWidth - window.innerWidth),
    ease: "none",
    scrollTrigger: {
    trigger: parentOfStickySection,
    pin: true,
    scrub: 1,
    invalidateOnRefresh: true,
    /* snap: 1 / (scrollSectionsAmount - 1), */
    end: () => "+=" + (translateXSection.scrollWidth - window.innerWidth),

    let anim = gsap.to(translateXSection, options);

    -old one:

    let options = {
    x: -(scrollSectionsAmount - 1) * window.innerWidth,
    ease: "none",
    scrollTrigger: {
    trigger: parentOfStickySection,
    pin: true,
    scrub: 1,
    /* snap: 1 / (scrollSectionsAmount - 1), */
    end: "+=" + (scrollSectionsAmount - 1) * window.innerWidth,

    let anim = gsap.to(translateXSection, options);

    By the way elementor 3.9 beta 2 breaks it (no scroll snapping and other issue) but I don't see any major changelog.



  5. Regarding my previous comment:

    1) code is just fine

    2) elementor 3.9 beta 2 breaks the design due to the added CSS transition rules to e-con
    (I don't know if it will be fixed in future because they added it to implement transform at a containel level)

    3) To fix it I simply added:

    .e-con {
    transition: none;

    1. Hey Shahar! Hope you are well!

      Indeed I see the issue... it looks like it's from some brand new CSS Elementor added in 3.9.0 ! They added a transition on the containers, for the "transform" property, and it results in this little glitch. I updated the code in the tutorial to avoid this issue.


      PS. Other readers seeing this: it's valid for the GSAP version only. Update your code if you have updated to 3.9.0 please.

  6. Hi Maxime,
    I just used this for a website and had a few issues. I'm using the GSAP version.
    1. On mobile, it's very jittering/lagging on scroll for some reasons.
    2. On both mobile and desktop, when scrolling horizontally to the last container, it doesn't stop at the very end of the last container first before scrolling down. Not sure if that makes sense.
    Can you help to take a look?

    1. Hi Jay Vu,

      I noticed that there are more recent versions of the two JS files available here which seem to make the scrolling "less jittery".


      However, when scrolling horizontally to the last container, it still (!) doesn't stop at the very end of the last container first before scrolling down.


    2. Hi Jay Vu,

      Try to enable snapping (see Maxime's comment above), that seems to make the last container's behavior better.


      1. Thank you Steffen! I'll try that out.

        Have you been able to make it work on mobile? I've tried but it was way too jumpy and jittery so I decided to disable in on mobile for now.

    3. Hey Jay Vu!

      1. Please see my answer below to Steffen
      2. Yea that's how GSAP works in this case... This is because of the easing. Try the straight JS version, this should be better, but there will be no easing.

      Hope this helps!


  7. Hi Maxime,

    I have the same issue that Jay Vu in his above comment is mentioning (i.e. littering and last container behavior). Could you please advise?

    Thank you!

    1. Hey Steffen,

      Yes indeed performance on mobile can be a challenge with this design. It will depend a lot on what's on your page, what's part of the content, and also simply the specs of the smartphone being used to view the page.

      Older smartphones in particular won't have a good UX. This is one of the con of this horizontal scrolling design. Use the straight JS version for optimal mobile performance (then you don't have easing though).

      That's one of the reason I explain how to disable this on mobile. Generally speaking, while it works there, it might not be ideal. Mostly if you have a heavy page already.


  8. Hi Jay Vu,

    I have switched it off on mobile because ß as you noticed as well - bit too jittery..

    Considering buying another plugin for that though (Premium Addons for Elementor)..



    1. Greetings Steffen!

      It's unlikely that the Premium Addons version will be more performant on mobile. It's using GSAP as well, so the results should be similar.

      The most performant version should in fact be the raw JS version I provide in this article. However, it does not have 'easing', so if you want that, you need the GSAP version.


  9. Hi Maxime,

    How can I make the next non-sticky, non-horizontal scrolling row show up faster?

    The way it is now, there is a big white space before the next section/container shows up (which isn't a horizontal scrolling section).

    I hope that makes sense, happy to provide further details!!

    Love the template, it's perfect for what I need it for. 🙂

    1. To readers: Daniel sent me his URL and I could have a look. The issue in this particular case was that he had changed, in the CSS provided, the "100vh" to "75vh", leaving 25vh of empty space at the bottom.

      Going back to 100vh fixed the issue.

  10. Hi Maxime, All working ok, but my website has a sticky nav, which disappears on the page where the horizontal code / feature is installed. I go live this thurs and really need the nav to show. Is there a fix you can help with?
    Thanks, Andrew

    1. Hey Kniyashava!

      Change this line:

      x: () => -(translateXSection.scrollWidth - window.innerWidth),


      x: () => (translateXSection.scrollWidth - window.innerWidth),

      I haven't tested but I think that should be it... let me know if that works!

Leave a Reply