Swipe Navigation/Carousel with Vue [Tutorial]

What’s in this tutorial

In this tutorial, we’ll start with a general overview of how this solution works. Then you’ll see a working JSFiddle with thoroughly commented code.

  • How it works
  • The code
  • Accessibility
  • Variations & options

What we’re building

We’re building a flexible component for swiping horizontally between pages or items. You can check out the demo, which is best tested on a mobile device.

GIF of JSFiddle demo


  • Native feel: Responsive to touch gestures, including quick swipes and longer pans
  • Lazy: Render as few items in the DOM as possible, so the total number of items doesn’t affect performance
  • Programmatic: With every swipe, we can update the route, send data to Analytics, etc.


  • JavaScript
  • Vue, but could be easily adapted for React
  • Hammer.JS touch library
  • Lodash debounce function or equivalent

How it works

Part 1: Displaying items

For this example, we have 6 items in our list. But we don’t want to render all of them in the DOM at once. We only want 3 items: the previous, current, and next items. We’ll figure out the rendered items by keeping track of the index for the current item.

items: [“red”, “orange”, “yellow”, “green”, “blue”, “purple”]
currentIndex: 4
renderedItems: [“green”, “blue”, “purple”]
currentIndex: 5
renderedItems: [“blue”, “purple”, “red”]
Three items rendered at once, but only one currently in view

Part 2: Adding movement

At this point, we could call our next or previous functions and the item displayed would change on the screen instantly. But our goal is to have the items slide nicely across the screen.

  1. We’ll listen for an event that tells us the transition is done. Then, we’ll change currentIndex so that the next item becomes the current one and a new next item is rendered

Part 3: Handling Touch events

We’re already translating items across the screen, so now we can sync that up with touch gestures.

Items translated a bit to the left to match gesture to go “next”
  • deltaY: Distance on the Y-axis
  • isFinal: Whether the gesture is over (finger or mouse lifted)
  • When the gesture is over, call the previous or next functions based on the direction, which will translate the item the rest of the way off the screen

Part 4: Handling additional scenarios

At this point, our component would work OK, but it would be pretty buggy. So we have a few scenarios to solve for:

  • Vertical gestures: We only want to respond to gestures that are clearly horizontal, so we’ll ignore gestures that are more vertical. But if a gesture starts horizontally and then goes diagonal, we’ll continue to react to the horizontal distance.
  • Too-frequent interactions: Things can get complicated if someone is swiping or tapping super quickly. The most straightforward way to deal with this is to not respond to taps or gestures while an item is still transitioning across the screen. Luckily this transition happens quickly enough (250ms) that it should still feel very responsive even if you’re swiping through quickly.
  • Duplicate events: Sometimes gestures will fire duplicate events, so we don’t want to end up skipping multiple slides at once. To avoid this, we’re debouncing our next and previous functions so that they are only called at most every 100ms.

Part 5: Adding tap targets (Optional)

Part 6: Adding edge effect for the end of the list (Optional)

What happens when a user tries to go to the next item when they’ve reached the end (or the previous item when they’re on the first)? Either the cards can go in an infinite loop, or we can stop the user from going forward or back when they reach one of the ends.

Edge shape expanded

The code

Here’s the complete example code in JSFiddle or a Codepen if you prefer.


For people using a keyboard or assistive technology to navigate, gestures likely aren’t going to be very accessible. In this example, I made the left and right tap targets accessible to a keyboard and labeled them as buttons. The bold attributes below were added for accessibility:

<div class="touch-tap-right" role="button" aria-label="Next" tabindex="0" @click="next" @keyup.enter="next" @keyup.space="next">

Variations & options

There are a lot of ways to customize this method and make it fit more use cases. Here a few ideas:

  • Add buttons for larger screens: In my app, I keep the cards the same width on larger screens but add arrow buttons for navigating
  • Show more than one item at a time: For larger screens or smaller items, you may want to show multiple items at once. To do this, you would need to update the logic for displaying items and how far you’re translating the items when going to the next or previous items

Thanks for reading!

Please let me know if you have any questions, find a mistake, or have ideas on how to improve. And give some claps if you found this useful!



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store