Menu

Tailwind CSS Scroll Snap Type

Scroll Snap Type is a property in CSS that allows developers to control the scroll behavior of a container, snapping child elements into place at defined scroll positions. It’s widely used for creating smooth, engaging user experiences, especially in carousels, galleries, and content scrolling containers.

In this guide, we will learn how to use Tailwind CSS to apply scroll snap properties easily without writing custom CSS manually.

ClassPropertiesExample
snap-nonescroll-snap-type: none;<div className="snap-none"></div>
snap-xscroll-snap-type: x var(--tw-scroll-snap-strictness);<div className="snap-x"></div>
snap-yscroll-snap-type: y var(--tw-scroll-snap-strictness);<div className="snap-y"></div>
snap-bothscroll-snap-type: both var(--tw-scroll-snap-strictness);<div className="snap-both"></div>
snap-mandatory--tw-scroll-snap-strictness: mandatory;<div className="snap-mandatory"></div>
snap-proximity--tw-scroll-snap-strictness: proximity;<div className="snap-proximity"></div>

Overview of Scroll Snap Type

Adding the Horizontal Scroll Snap

To define scroll snapping, Tailwind provides scroll-snap utilities like snap-none, snap-x, snap-y, and snap-both. These classes describe the direction of the scroll snapping behavior.

Here's an example of horizontal scroll snap:

This is a live editor. Play around with it!
// React carousel demonstrating horizontal scroll snapping 
export default function HorizontalCarousel() {
  return (
   <div className="snap-x snap-mandatory w-full overflow-x-scroll flex">
      <div className="h-screen w-screen bg-blue-200 flex justify-center items-center flex-shrink-0 snap-center p-4">Scroll Horizontally</div>
      <div className="h-screen w-screen bg-yellow-200 flex justify-center items-center flex-shrink-0 snap-center p-4">Scroll Horizontally</div>
      <div className="h-screen w-screen bg-red-200 flex justify-center items-center flex-shrink-0 snap-center p-4">Scroll Horizontally</div>
    </div>
  );
}

Adding the Mandatory Scroll Snap

The snap-mandatory utility strictly enforces the scroll snap behavior and ensures that the viewport strictly adheres to snap points.

This is a live editor. Play around with it!
// Mandatory snapping behavior 
export default function MandatorySnapping() {
  return (
    <div className="snap-y snap-mandatory h-screen w-screen overflow-y-scroll bg-gray-900">
      <div className="snap-center flex items-center justify-center h-screen bg-blue-200">Scroll Below</div>
      <div className="snap-center flex items-center justify-center h-screen bg-green-200">Scroll Below</div>
      <div className="snap-center flex items-center justify-center h-screen bg-red-200">Scroll Above</div>
    </div>
  );
}

Adding the Proximity Scrolling Snap

You can achieve a more lenient snapping behavior using snap-proximity. It snaps elements only when the scroll end position is close enough to a snap point.

This is a live editor. Play around with it!
// Proximity snapping example
export default function ProximityScroll() {
  return (
    <div className="snap-y snap-proximity h-screen w-screen overflow-y-scroll">
      <div className="snap-center flex items-center justify-center h-screen bg-blue-200">Scroll Below</div>
      <div className="snap-center flex items-center justify-center h-screen bg-green-200">Scroll Below</div>
      <div className="snap-center flex items-center justify-center h-screen bg-red-200">Scroll Above</div>
    </div>
  );
}

States and Responsiveness

Tailwind’s utility classes allow you to conditionally apply the scroll snapping using hover, focus states, and breakpoints. This section walks through how to integrate this flexibility into your projects.

Hover and Focus States

Tailwind’s scroll snap utilities also work on states like hover and focus. Use hover and modifiers while adding these utilities to make them work on these states.

This is a live editor. Play around with it!
// Scroll snap enabled on hover
export default function HoverSnap() {
  return (
    <div className="group">
      <div className="snap-y snap-mandatory hover:snap-proximity h-screen overflow-y-scroll bg-white">
        <div className="snap-center flex items-center justify-center h-screen bg-blue-200">Scroll Below</div>
        <div className="snap-center flex items-center justify-center h-screen bg-green-200">Scroll Below</div>
        <div className="snap-center flex items-center justify-center h-screen bg-red-200">Scroll Above</div>
      </div>
    </div>
  );
}

Breakpoint Modifiers

Different scroll snap types can be used for different screens with responsive modifiers like sm, md, lg, etc.

This is a live editor. Play around with it!
// Scroll snapping with responsive breakpoints
export default function ResponsiveSnapping() {
  return (
    <div className="snap-y md:snap-mandatory lg:snap-proximity h-screen overflow-y-scroll bg-white">
      <div className="snap-center flex items-center justify-center h-screen bg-blue-200">Scroll Below</div>
      <div className="snap-center flex items-center justify-center h-screen bg-green-200">Scroll Below</div>
      <div className="snap-center flex items-center justify-center h-screen bg-red-200">Scroll Above</div>
    </div>
  );
}

Real World Examples

A horizontal product showcase with mandatory snap points, perfect for e-commerce featured items.

This is a live editor. Play around with it!
export default function ProductShowcase() {
  const products = [
    {
      id: 1,
      name: "Premium Leather Watch",
      price: "$299",
      src: "https://images.unsplash.com/photo-1523275335684-37898b6baf30",
      alt: "Premium leather watch with gold dial"
    },
    {
      id: 2,
      name: "Wireless Headphones",
      price: "$199",
      src: "https://images.unsplash.com/photo-1505740420928-5e560c06d30e",
      alt: "Black wireless headphones"
    },
    {
      id: 3,
      name: "Smart Speaker",
      price: "$149",
      src: "https://images.unsplash.com/photo-1589492477829-5e65395b66cc",
      alt: "Modern smart speaker"
    },
    {
      id: 4,
      name: "Mechanical Keyboard",
      price: "$129",
      src: "https://images.unsplash.com/photo-1511467687858-23d96c32e4ae",
      alt: "RGB mechanical keyboard"
    },
    {
      id: 5,
      name: "Fitness Tracker",
      price: "$89",
      src: "https://images.unsplash.com/photo-1575311373937-040b8e1fd5b6",
      alt: "Smart fitness tracker"
    },
    {
      id: 6,
      name: "Wireless Charger",
      price: "$49",
      src: "https://images.unsplash.com/photo-1586816879360-004f5b0c51e3",
      alt: "Wireless charging pad"
    }
  ];

  return (
    <div className="w-full overflow-x-scroll snap-x snap-mandatory">
      <div className="flex">
        {products.map((product) => (
          <div key={product.id} className="w-80 flex-shrink-0 snap-center p-4">
            <img 
              src={product.src} 
              alt={product.alt}
              className="w-full h-64 object-cover rounded-lg"
            />
            <h3 className="mt-2 text-lg font-semibold">{product.name}</h3>
            <p className="text-blue-600">{product.price}</p>
          </div>
        ))}
      </div>
    </div>
  );
}

Vertical Story Viewer

A vertical story viewer with smooth snapping, similar to social media stories.

This is a live editor. Play around with it!
export default function StoryViewer() {
  const stories = [
    {
      id: 1,
      username: "traveler_jane",
      src: "https://images.unsplash.com/photo-1469474968028-56623f02e42e",
      alt: "Mountain landscape",
      location: "Swiss Alps"
    },
    {
      id: 2,
      username: "foodie_john",
      src: "https://images.unsplash.com/photo-1565299624946-b28f40a0ae38",
      alt: "Gourmet pizza",
      location: "Rome, Italy"
    },
    {
      id: 3,
      username: "urban_explorer",
      src: "https://images.unsplash.com/photo-1449824913935-59a10b8d2000",
      alt: "City skyline",
      location: "Tokyo, Japan"
    },
    {
      id: 4,
      username: "beach_lover",
      src: "https://images.unsplash.com/photo-1507525428034-b723cf961d3e",
      alt: "Tropical beach",
      location: "Maldives"
    },
    {
      id: 5,
      username: "wild_photographer",
      src: "https://images.unsplash.com/photo-1546182990-dffeafbe841d",
      alt: "Wildlife photo",
      location: "Serengeti"
    },
    {
      id: 6,
      username: "architecture_enthusiast",
      src: "https://images.unsplash.com/photo-1494522358652-f30e61a60313",
      alt: "Modern building",
      location: "Dubai, UAE"
    }
  ];

  return (
    <div className="h-screen overflow-y-scroll snap-y snap-mandatory">
      {stories.map((story) => (
        <div 
          key={story.id} 
          className="h-screen w-full flex-shrink-0 snap-start relative"
        >
          <img 
            src={story.src} 
            alt={story.alt}
            className="w-full h-full object-cover"
          />
          <div className="absolute bottom-0 left-0 right-0 p-6 bg-gradient-to-t from-black/60 text-white">
            <p className="font-bold text-xl">@{story.username}</p>
            <p className="text-sm">{story.location}</p>
          </div>
        </div>
      ))}
    </div>
  );
}

A grid-based portfolio with both horizontal and vertical snapping.

This is a live editor. Play around with it!
export default function PortfolioGrid() {
  const projects = [
    {
      id: 1,
      title: "Brand Identity Design",
      category: "Branding",
      src: "https://images.unsplash.com/photo-1634942537034-2531766767d1",
      alt: "Brand identity mockup"
    },
    {
      id: 2,
      title: "Mobile App UI",
      category: "UI/UX",
      src: "https://images.unsplash.com/photo-1616469829941-c7200edec809",
      alt: "Mobile app interface"
    },
    {
      id: 3,
      title: "Product Photography",
      category: "Photography",
      src: "https://images.unsplash.com/photo-1441986300917-64674bd600d8",
      alt: "Product photo setup"
    },
    {
      id: 4,
      title: "Web Development",
      category: "Development",
      src: "https://images.unsplash.com/photo-1461749280684-dccba630e2f6",
      alt: "Code editor screenshot"
    },
    {
      id: 5,
      title: "3D Modeling",
      category: "3D Design",
      src: "https://images.unsplash.com/photo-1615648178124-01f7162ceac4",
      alt: "3D render"
    },
    {
      id: 6,
      title: "Motion Graphics",
      category: "Animation",
      src: "https://images.unsplash.com/photo-1550751827-4bd374c3f58b",
      alt: "Animation frame"
    }
  ];

  return (
    <div className="grid gap-4 h-screen overflow-scroll snap-both snap-mandatory p-4">
      {projects.map((project) => (
        <div 
          key={project.id}
          className="snap-start bg-white rounded-xl overflow-hidden h-72 shadow-lg"
        >
          <img 
            src={project.src} 
            alt={project.alt}
            className="w-full h-48 object-cover"
          />
          <div className="p-4">
            <span className="text-sm text-blue-500">{project.category}</span>
            <h3 className="text-lg font-bold mt-1">{project.title}</h3>
          </div>
        </div>
      ))}
    </div>
  );
}

Timeline Scroll Experience

A vertical timeline with snap points for each major event.

This is a live editor. Play around with it!
export default function Timeline() {
  const events = [
    {
      id: 1,
      year: "2023",
      title: "Company IPO",
      description: "Successfully went public on NYSE",
      icon: "https://images.unsplash.com/photo-1611974789855-9c2a0a7236a3",
      alt: "Stock market graph"
    },
    {
      id: 2,
      year: "2021",
      title: "Global Expansion",
      description: "Opened offices in 15 countries",
      icon: "https://images.unsplash.com/photo-1526304640581-d334cdbbf45e",
      alt: "World map"
    },
    {
      id: 3,
      year: "2019",
      title: "Product Launch",
      description: "Released flagship product",
      icon: "https://images.unsplash.com/photo-1460925895917-afdab827c52f",
      alt: "Product launch"
    },
    {
      id: 4,
      year: "2017",
      title: "First Investment",
      description: "Secured Series A funding",
      icon: "https://images.unsplash.com/photo-1559526324-4b87b5e36e44",
      alt: "Investment graph"
    },
    {
      id: 5,
      year: "2015",
      title: "Team Growth",
      description: "Reached 100 employees",
      icon: "https://images.unsplash.com/photo-1522071820081-009f0129c71c",
      alt: "Team photo"
    },
    {
      id: 6,
      year: "2013",
      title: "Company Founded",
      description: "Started in a garage",
      icon: "https://images.unsplash.com/photo-1497366216548-37526070297c",
      alt: "Startup office"
    }
  ];

  return (
    <div className="h-screen overflow-y-scroll snap-y snap-mandatory">
      {events.map((event) => (
        <div 
          key={event.id}
          className="h-screen flex items-center snap-start p-8 bg-gradient-to-b from-gray-50 to-white"
        >
          <div className="max-w-2xl mx-auto flex gap-8 items-center">
            <div className="w-24 h-24 rounded-full overflow-hidden flex-shrink-0">
              <img 
                src={event.icon} 
                alt={event.alt}
                className="w-full h-full object-cover"
              />
            </div>
            <div>
              <span className="text-4xl font-bold text-blue-600">{event.year}</span>
              <h3 className="text-2xl font-bold mt-2">{event.title}</h3>
              <p className="text-gray-600 mt-2">{event.description}</p>
            </div>
          </div>
        </div>
      ))}
    </div>
  );
}

A fullscreen image gallery with diagonal snapping for an unique viewing experience.

This is a live editor. Play around with it!
export default function ImageGallery() {
  const images = [
    {
      id: 1,
      title: "Northern Lights",
      location: "Iceland",
      src: "https://images.unsplash.com/photo-1531366936337-7c912a4589a7",
      alt: "Aurora borealis in night sky"
    },
    {
      id: 2,
      title: "Desert Sunset",
      location: "Sahara",
      src: "https://images.unsplash.com/photo-1509316785289-025f5b846b35",
      alt: "Golden desert sunset"
    },
    {
      id: 3,
      title: "Rainforest Canopy",
      location: "Amazon",
      src: "https://images.unsplash.com/photo-1469125155630-7ed37e065743",
      alt: "Aerial view of rainforest"
    },
    {
      id: 4,
      title: "Mountain Peak",
      location: "Himalayas",
      src: "https://images.unsplash.com/photo-1464822759023-fed622ff2c3b",
      alt: "Snow-capped mountain peak"
    },
    {
      id: 5,
      title: "Ocean Waves",
      location: "Pacific",
      src: "https://images.unsplash.com/photo-1505118380757-91f5f5632de0",
      alt: "Crashing ocean waves"
    },
    {
      id: 6,
      title: "City Lights",
      location: "Hong Kong",
      src: "https://images.unsplash.com/photo-1506318137071-a8e063b4bec0",
      alt: "Night city skyline"
    }
  ];

  return (
    <div className="h-screen grid gap-1 overflow-scroll snap-both snap-mandatory">
      {images.map((image) => (
        <div 
          key={image.id}
          className="relative aspect-square snap-start"
        >
          <img 
            src={image.src} 
            alt={image.alt}
            className="w-full h-full object-cover"
          />
          <div className="absolute inset-0 bg-black/30 opacity-0 hover:opacity-100 transition-opacity duration-300">
            <div className="absolute bottom-4 left-4 text-white">
              <h3 className="text-xl font-bold">{image.title}</h3>
              <p className="text-sm">{image.location}</p>
            </div>
          </div>
        </div>
      ))}
    </div>
  );
}

Best Practices

Maintain Design Consistency

Design consistency is essential when leveraging Tailwind CSS’s Scroll Snap Type utilities. To maintain uniformity, ensure that all scrollable containers and their child elements use the same snapping logic throughout your project.

For example, if a gallery or carousel uses snap-x snap-mandatory, apply the same utility classes to similar components. This approach strengthens the visual flow and ensures all components behave predictably.

Leverage Utility Combinations

Combining Scroll Snap Type utilities with complementary Tailwind classes can introduce advanced designs optimized for functionality and style. For example, pairing snap-x with flex or grid classes ensures seamless snapping alongside sophisticated layout arrangements.

Add spacing utilities like gap-4 or padding classes such as p-4 to maintain aesthetic spacing between snap elements.

Accessibility Considerations

Enhance Readability and Navigability

Scroll Snap Type improves navigability by creating predictable stopping points for scrolling containers. However, to better serve users with visual impairments or motor difficulties, content within snapping sections must remain clear and easy to explore. Maintain sufficient spacing, consistent alignment, and readable fonts.

Support content contextually by including visible or semantic indicators for scrollable components. For example, in a horizontal carousel using snap-x, add clear scroll instructions (e.g., aria-label attributes or visible scroll icons) to help users unfamiliar with snap-based navigation.

Ensure Keyboard Accessibility

Keyboard accessibility relies on two main factors: an intuitive focus order and visible indicators. Any content that can be snapped or interacted with should support tabindex to become focusable, so keyboard users can navigate through each snap point with the tab key. Additionally, combine it with outline utilities to provide visual feedback once an element is in focus.

This not only helps users understand their position on the page but also enhances usability by ensuring they know which snapable item is currently selected. In carousel layouts or horizontally scrolling sections, these principles become even more critical, as keyboard navigation without proper focus styling often leads to confusion or lost context.