Menu

Tailwind CSS Scroll Snap Align

Scroll Snap Align allows you to define how scrollable elements snap into place when scrolling stops. It lets you control the alignment of child elements within a container in both horizontal and vertical scroll contexts. Tailwind CSS provides a variety of pre-built utilities to work with scroll snap align, allowing you to create responsive and interactive user interfaces with minimal effort.

Below, we'll explore the implementation of scroll-snap-align using Tailwind CSS utilities, helping you efficiently apply snapping behavior to their elements.

ClassPropertiesExample
snap-startscroll-snap-align: start;<div className="snap-start"></div>
snap-endscroll-snap-align: end;<div className="snap-end"></div>
snap-centerscroll-snap-align: center;<div className="snap-center"></div>
snap-align-nonescroll-snap-align: none;<div className="snap-align-none"></div>

Overview of Scroll Snap Align

Snap Center

To make items align to the center of a scrollable container, use the snap-center utility.

This is a live editor. Play around with it!
import React from "react";  

export default function CenterAlignment() {  
  return (  
    <div className="snap-y snap-mandatory flex flex-col gap-4 items-center overflow-auto h-screen px-10 pt-10 bg-gray-50">
      <div className="snap-center h-96">
        <img
          src="https://images.unsplash.com/photo-1511467687858-23d96c32e4ae"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
      <div className="snap-center h-96">
        <img
          src="https://images.unsplash.com/photo-1487017159836-4e23ece2e4cf"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
      <div className="snap-center h-96">
        <img
          src="https://images.unsplash.com/photo-1508873699372-7aeab60b44ab"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
      <div className="snap-center h-96">
        <img
          src="https://images.unsplash.com/photo-1489269637500-aa0e75768394"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
      <div className="snap-center h-96">
        <img
          src="https://images.unsplash.com/photo-1488998427799-e3362cec87c3"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
      <div className="snap-center h-96">
        <img
          src="https://images.unsplash.com/photo-1527443224154-c4a3942d3acf"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
    </div>
  );  
}

Snap Start

To make items align to the start of a scrollable container, use the snap-start utility.

This is a live editor. Play around with it!
import React from "react";  

export default function StartAlignment() {  
  return (  
    <div className="snap-y snap-mandatory flex flex-col gap-4 items-center overflow-auto h-screen px-10 pt-10 bg-gray-50 py-60">
      <div className="snap-start h-96">
        <img
          src="https://images.unsplash.com/photo-1511467687858-23d96c32e4ae"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
      <div className="snap-start h-96">
        <img
          src="https://images.unsplash.com/photo-1487017159836-4e23ece2e4cf"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
      <div className="snap-start h-96">
        <img
          src="https://images.unsplash.com/photo-1508873699372-7aeab60b44ab"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
      <div className="snap-start h-96">
        <img
          src="https://images.unsplash.com/photo-1489269637500-aa0e75768394"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
      <div className="snap-start h-96">
        <img
          src="https://images.unsplash.com/photo-1488998427799-e3362cec87c3"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
      <div className="snap-start h-96">
        <img
          src="https://images.unsplash.com/photo-1527443224154-c4a3942d3acf"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
    </div>
  );  
}

Snap End

To make items align to the end of a scrollable container, use the snap-end utility.

This is a live editor. Play around with it!
import React from "react";  

export default function EndAlignment() {  
  return (  
    <div className="snap-y snap-mandatory flex flex-col gap-4 items-center overflow-auto h-screen px-10 pt-10 bg-gray-50 py-60">
      <div className="snap-end h-96">
        <img
          src="https://images.unsplash.com/photo-1511467687858-23d96c32e4ae"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
      <div className="snap-end h-96">
        <img
          src="https://images.unsplash.com/photo-1487017159836-4e23ece2e4cf"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
      <div className="snap-end h-96">
        <img
          src="https://images.unsplash.com/photo-1508873699372-7aeab60b44ab"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
      <div className="snap-end h-96">
        <img
          src="https://images.unsplash.com/photo-1489269637500-aa0e75768394"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
      <div className="snap-end h-96">
        <img
          src="https://images.unsplash.com/photo-1488998427799-e3362cec87c3"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
      <div className="snap-end h-96">
        <img
          src="https://images.unsplash.com/photo-1527443224154-c4a3942d3acf"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
    </div>
  );  
}

States and Responsiveness

Hover and Focus States

Scroll Snap Align in Tailwind doesn't just stop at static application—it also supports dynamic effects driven by states like hover and focus with the help of state modifiers, e.g, hover:snap-center, focus:snap-end, etc.

In the below example, hover on an image to align it to the center:

This is a live editor. Play around with it!
import React from "react";  

export default function HoverState() {  
  return (
    <div className="snap-y snap-mandatory flex flex-col gap-4 items-center overflow-auto h-screen px-10 pt-10 bg-gray-50 py-60">
      <div className="snap-start hover:snap-center h-96">
        <img
          src="https://images.unsplash.com/photo-1511467687858-23d96c32e4ae"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
      <div className="snap-start hover:snap-center h-96">
        <img
          src="https://images.unsplash.com/photo-1487017159836-4e23ece2e4cf"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
      <div className="snap-start hover:snap-center h-96">
        <img
          src="https://images.unsplash.com/photo-1508873699372-7aeab60b44ab"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
      <div className="snap-start hover:snap-center h-96">
        <img
          src="https://images.unsplash.com/photo-1489269637500-aa0e75768394"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
      <div className="snap-start hover:snap-center h-96">
        <img
          src="https://images.unsplash.com/photo-1488998427799-e3362cec87c3"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
      <div className="snap-start hover:snap-center h-96">
        <img
          src="https://images.unsplash.com/photo-1527443224154-c4a3942d3acf"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
    </div>
  );  
}

Breakpoint Modifiers

Tailwind's flexibility also extends to responsive design by using breakpoint modifiers like sm, md, and lg. Below is an implementation where snap behaviors shift based on the screen size:

This is a live editor. Play around with it!
import React from "react";  

export default function BreakpointAlignment() {  
  return (  
        <div className="snap-y snap-mandatory flex flex-col gap-4 items-center overflow-auto h-screen px-10 pt-10 bg-gray-50 py-60">
      <div className="snap-start sm:snap-center lg:snap-end  h-96">
        <img
          src="https://images.unsplash.com/photo-1511467687858-23d96c32e4ae"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
      <div className="snap-start sm:snap-center lg:snap-end h-96">
        <img
          src="https://images.unsplash.com/photo-1487017159836-4e23ece2e4cf"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
      <div className="snap-start sm:snap-center lg:snap-end h-96">
        <img
          src="https://images.unsplash.com/photo-1508873699372-7aeab60b44ab"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
      <div className="snap-start sm:snap-center lg:snap-end h-96">
        <img
          src="https://images.unsplash.com/photo-1489269637500-aa0e75768394"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
      <div className="snap-start sm:snap-center lg:snap-end h-96">
        <img
          src="https://images.unsplash.com/photo-1488998427799-e3362cec87c3"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
      <div className="snap-start sm:snap-center lg:snap-end h-96">
        <img
          src="https://images.unsplash.com/photo-1527443224154-c4a3942d3acf"
          className="w-full h-full object-cover shadow-md"
        />
      </div>
    </div>
  );  
}

Real World Examples

A horizontal product showcase with snap scrolling, perfect for e-commerce product galleries. Each product snaps to the center of the viewport.

This is a live editor. Play around with it!
export default function ProductGallery() {
  const products = [
    {
      id: 1,
      name: "Premium Leather Bag",
      price: "$299.99",
      src: "https://images.unsplash.com/photo-1547949003-9792a18a2601",
      alt: "Brown leather messenger bag"
    },
    {
      id: 2,
      name: "Classic Watch",
      price: "$199.99",
      src: "https://images.unsplash.com/photo-1524592094714-0f0654e20314",
      alt: "Silver analog watch"
    },
    {
      id: 3,
      name: "Wireless Headphones",
      price: "$159.99",
      src: "https://images.unsplash.com/photo-1505740420928-5e560c06d30e",
      alt: "Black wireless headphones"
    },
    {
      id: 4,
      name: "Smart Speaker",
      price: "$129.99",
      src: "https://images.unsplash.com/photo-1589492477829-5e65395b66cc",
      alt: "Modern smart speaker"
    },
    {
      id: 5,
      name: "Fitness Tracker",
      price: "$89.99",
      src: "https://images.unsplash.com/photo-1575311373937-040b8e1fd5b6",
      alt: "Black fitness band"
    },
  ];

  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-bold">{product.name}</h3>
            <p className="text-gray-600">{product.price}</p>
          </div>
        ))}
      </div>
    </div>
  );
}

Vertical Story Viewer

A social media story viewer with vertical snap scrolling, similar to Instagram or Facebook 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",
      timestamp: "2h ago"
    },
    {
      id: 2,
      username: "foodie_john",
      src: "https://images.unsplash.com/photo-1565299624946-b28f40a0ae38",
      alt: "Gourmet dish",
      timestamp: "4h ago"
    },
    {
      id: 3,
      username: "pet_lover",
      src: "https://images.unsplash.com/photo-1514888286974-6c03e2ca1dba",
      alt: "Cat portrait",
      timestamp: "6h ago"
    },
    {
      id: 4,
      username: "urban_explorer",
      src: "https://images.unsplash.com/photo-1449824913935-59a10b8d2000",
      alt: "City skyline",
      timestamp: "8h ago"
    },
    {
      id: 5,
      username: "art_enthusiast",
      src: "https://images.unsplash.com/photo-1579783902614-a3fb3927b6a5",
      alt: "Street art",
      timestamp: "10h ago"
    },
    {
      id: 6,
      username: "nature_photographer",
      src: "https://images.unsplash.com/photo-1472214103451-9374bd1c798e",
      alt: "Sunset view",
      timestamp: "12h ago"
    }
  ];

  return (
    <div className="h-screen overflow-y-scroll snap-y snap-mandatory">
      {stories.map((story) => (
        <div key={story.id} className="h-screen snap-start relative">
          <img 
            src={story.src} 
            alt={story.alt} 
            className="w-full h-full object-cover"
          />
          <div className="absolute bottom-8 left-4 text-white">
            <p className="text-xl font-bold">{story.username}</p>
            <p className="text-sm opacity-75">{story.timestamp}</p>
          </div>
        </div>
      ))}
    </div>
  );
}

Portfolio Project Showcase

A horizontal scrolling portfolio with projects snapping to the start of the viewport.

This is a live editor. Play around with it!
export default function PortfolioShowcase() {
  const projects = [
    {
      id: 1,
      title: "E-commerce Platform",
      category: "Web Development",
      src: "https://images.unsplash.com/photo-1661956602116-aa6865609028",
      alt: "E-commerce website mockup",
      description: "Full-stack online shopping platform"
    },
    {
      id: 2,
      title: "Mobile Banking App",
      category: "Mobile Design",
      src: "https://images.unsplash.com/photo-1563986768609-322da13575f3",
      alt: "Banking app interface",
      description: "Secure banking application"
    },
    {
      id: 3,
      title: "Restaurant Website",
      category: "UI/UX Design",
      src: "https://images.unsplash.com/photo-1517248135467-4c7edcad34c4",
      alt: "Restaurant website design",
      description: "Modern restaurant web presence"
    },
    {
      id: 4,
      title: "Fitness Tracker",
      category: "Mobile Development",
      src: "https://images.unsplash.com/photo-1576678927484-cc907957088c",
      alt: "Fitness app interface",
      description: "Health monitoring application"
    },
    {
      id: 5,
      title: "Social Network",
      category: "Web Application",
      src: "https://images.unsplash.com/photo-1611162617213-7d7a39e9b1d7",
      alt: "Social network platform",
      description: "Community platform design"
    },
    {
      id: 6,
      title: "Travel Blog",
      category: "Content Platform",
      src: "https://images.unsplash.com/photo-1501785888041-af3ef285b470",
      alt: "Travel blog design",
      description: "Travel content management system"
    }
  ];

  return (
    <div className="w-full overflow-x-scroll snap-x snap-mandatory">
      <div className="flex">
        {projects.map((project) => (
          <div key={project.id} className="w-screen flex-shrink-0 snap-start p-8">
            <div className="max-w-4xl mx-auto bg-white rounded-xl shadow-lg overflow-hidden">
              <div className="md:flex">
                <div className="md:flex-shrink-0">
                  <img 
                    className="h-36 w-full object-cover md:w-48" 
                    src={project.src} 
                    alt={project.alt}
                  />
                </div>
                <div className="p-8">
                  <div className="text-sm text-indigo-500">{project.category}</div>
                  <h2 className="text-2xl font-bold mt-2">{project.title}</h2>
                  <p className="mt-4 text-gray-500">{project.description}</p>
                </div>
              </div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

A responsive image gallery with vertical snap scrolling.

This is a live editor. Play around with it!
export default function ImageGalleryGrid() {
  const images = [
    {
      id: 1,
      src: "https://images.unsplash.com/photo-1682687220067-dced9a881b56",
      alt: "Abstract art piece",
      category: "Abstract"
    },
    {
      id: 2,
      src: "https://images.unsplash.com/photo-1682687221006-b7fd60cf9dd0",
      alt: "Portrait photography",
      category: "Portrait"
    },
    {
      id: 3,
      src: "https://images.unsplash.com/photo-1506744038136-46273834b3fb",
      alt: "Landscape photo",
      category: "Landscape"
    },
    {
      id: 4,
      src: "https://images.unsplash.com/photo-1682687220199-d0124f48f95b",
      alt: "Street photography",
      category: "Street"
    },
    {
      id: 5,
      src: "https://images.unsplash.com/photo-1682687220509-61b8a906ca19",
      alt: "Wildlife photo",
      category: "Wildlife"
    },
    {
      id: 6,
      src: "https://images.unsplash.com/photo-1682687220923-c58b9a4592ae",
      alt: "Architecture photo",
      category: "Architecture"
    }
  ];

  return (
    <div className="grid grid-cols-2 gap-4 h-screen overflow-scroll snap-y snap-mandatory">
      {images.map((image, index) => (
        <div 
          key={image.id} 
          className={`snap-start ${index % 3 === 0 ? 'col-span-2' : 'col-span-1'}`}
        >
          <div className="relative group">
            <img 
              src={image.src} 
              alt={image.alt} 
              className="w-full h-64 object-cover rounded-lg"
            />
            <div className="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-50 transition-opacity duration-300">
              <div className="absolute bottom-4 left-4 text-white opacity-0 group-hover:opacity-100 transition-opacity duration-300">
                <p className="text-lg font-bold">{image.category}</p>
              </div>
            </div>
          </div>
        </div>
      ))}
    </div>
  );
}

A feature showcase with horizontal snap scrolling and centered items.

This is a live editor. Play around with it!
export default function FeatureCarousel() {
  const features = [
    {
      id: 1,
      title: "Cloud Storage",
      icon: "https://images.unsplash.com/photo-1590859808308-3d2d9c515b1a",
      description: "Secure cloud storage for all your files",
      color: "bg-blue-100"
    },
    {
      id: 2,
      title: "File Sharing",
      icon: "https://images.unsplash.com/photo-1576091160399-112ba8d25d1d",
      description: "Easy and secure file sharing",
      color: "bg-green-100"
    },
    {
      id: 3,
      title: "Collaboration",
      icon: "https://images.unsplash.com/photo-1522071820081-009f0129c71c",
      description: "Real-time collaboration tools",
      color: "bg-yellow-100"
    },
    {
      id: 4,
      title: "Analytics",
      icon: "https://images.unsplash.com/photo-1551288049-bebda4e38f71",
      description: "Detailed usage analytics",
      color: "bg-purple-100"
    },
    {
      id: 5,
      title: "Security",
      icon: "https://images.unsplash.com/photo-1563986768494-4dee2763ff3f",
      description: "Enterprise-grade security",
      color: "bg-red-100"
    },
    {
      id: 6,
      title: "Integration",
      icon: "https://images.unsplash.com/photo-1573164713988-8665fc963095",
      description: "Seamless app integration",
      color: "bg-indigo-100"
    }
  ];

  return (
    <div className="w-full overflow-x-scroll snap-x snap-mandatory">
      <div className="flex">
        {features.map((feature) => (
          <div 
            key={feature.id} 
            className="w-80 flex-shrink-0 snap-center p-6"
          >
            <div className={`rounded-xl p-6 ${feature.color}`}>
              <div className="w-16 h-16 rounded-full overflow-hidden mb-4">
                <img 
                  src={feature.icon} 
                  alt={feature.title} 
                  className="w-full h-full object-cover"
                />
              </div>
              <h3 className="text-xl font-bold mb-2">{feature.title}</h3>
              <p className="text-gray-600">{feature.description}</p>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

Best Practices

Maintain Design Consistency

Maintaining design consistency with Scroll Snap Align in Tailwind CSS ensures a consistent user experience, especially in interactive scrolling interfaces. To achieve uniformity, use Tailwind's utility classes like snap-start, snap-center, and snap-end across similar components or sections.

For example, an e-commerce website with horizontal scrolling product galleries can apply snap-center to ensure each product aligns uniformly when scrolling stops. This repetition in utilities fosters predictable behavior and improves the overall aesthetics of the interface.

Optimize for Reusability

Reusable components boost both scalability and maintainability across projects. By designing scroll-snapped elements as modular components, you can reuse them without duplicating styles. Tailwind’s utility-first approach makes it easy to adjust snapped behavior quickly while keeping components flexible.

One effective method is to encapsulate scroll-snapped elements within reusable components. For instance, a scrollable card component using snap-center can be consistently deployed across various sections. Snap utilities can also be used as props to tweak the snapping behavior without altering the core structure.

Accessibility Considerations

Enhance Readability and Navigability

Ensuring readability and navigability simplifies how users interact with a scrollable page. Scroll Snap Align is instrumental in guiding users, especially in scroll-heavy contexts like timelines, image galleries, or horizontal lists. Tailwind CSS utilities like snap-center facilitate visually centered alignment, which improves the user's focus by pointing them to the primary element currently in view. This reduces cognitive effort during scrolling.

Furthermore, pairing snapping utilities with well-thought-out spacing and typography classes ensures that content remains accessible while users scroll. For instance, a snap-aligned timeline with properly spaced headers or captions ensures a clean and user-friendly flow. This makes long-form content approachable for readers of varying literacy levels, keeping them engaged with minimal friction.

Ensure Keyboard Accessibility

Keyboard navigation is a fundamental accessibility consideration when using scroll-snap-align. Users relying on keyboard navigation should be able to traverse snapped sections effortlessly without encountering unexpected behaviors.

A recommended approach is ensuring that all snapped elements are focusable. Apply tabindex on key sections to allow keyboard users to navigate snapped content smoothly. Additionally, use rings or outlines on focus to ensure that focused elements remain distinguishable.

Another effective technique is implementing anchor-based navigation. Providing a jump-to-section feature to allow users to navigate snapped elements efficiently using keyboard.