DEV Community

Bartłomiej Stefański
Bartłomiej Stefański

Posted on • Originally published at bstefanski.com on

How to implement horizontal media scroller component in React with CSS only

It is a common practice to render list in form of horizontal swipeable carousel on mobile resolutions. For that I created a simple, almost no-JS component for that. It uses JavaScript only for centering the overflow container.

A preview of media scroller component

I used styled-components and styled-breakpoints for this, but you can easily transform this to any other CSS-in-JS approach.

import { Container } from 'components/Container';  
import { useEffect, useRef } from 'react';  
import { down } from 'styled-breakpoints';   
import styled from 'styled-components';  

const GridContainer = styled(Container)`  
  ${down('sm')} {  
    max-width: 100%;  
    margin: unset;  
    padding: 0;  
  }  
`;  

const Grid = styled.div<{ desktopMinWidth: string; mobileMinWidth: string }>`  
  display: grid;  
  grid-template-columns: ${({ desktopMinWidth }) => `repeat(auto-fill, minmax(${desktopMinWidth}, 1fr))`};  
  grid-gap: 18px;  

  ${down('sm')} {  
    padding: 0 20px;  
    position: relative;  

    cursor: grab;  
    scrollbar-width: none;  
    -ms-overflow-style: none;  

    ::-webkit-scrollbar {  
      width: 0px;  
      background: transparent;  
    }  

    display: flex;  
    overflow-x: auto;  
    scroll-snap-type: x mandatory;  
    -webkit-overflow-scrolling: touch;  

    & > * {  
      min-width: ${({ mobileMinWidth }) => mobileMinWidth};  
      max-width: ${({ mobileMinWidth }) => mobileMinWidth};  
      scroll-snap-align: center;  
    }  
  }  
`;  

interface SwipeableGridProps {  
  mobileMinWidth: string;  
  desktopMinWidth: string;  
  children: React.ReactNode;  
  className?: string;  
}  

export const SwipeableGrid = ({  
  mobileMinWidth,  
  desktopMinWidth,  
  children,  
  className,  
}: SwipeableGridProps) => {  
  const swipeableGridRef = useRef<HTMLDivElement | null>(null);  

  useEffect(() => {  
    const currentEl = swipeableGridRef.current;  
    if (currentEl) {  
      currentEl.scrollLeft = currentEl.clientWidth / 2;  
    }  
  }, []);  

  return (  
    <GridContainer>   
      <Grid  
        desktopMinWidth={desktopMinWidth}  
        mobileMinWidth={mobileMinWidth}  
        ref={swipeableGridRef}  
        className={className}  
      >  
        {children}  
      </Grid>  
    </GridContainer>  
  );  
};
Enter fullscreen mode Exit fullscreen mode

Top comments (0)