Create Tik-Tok/Youtube Shorts like snap infinite scroll - React
Tags: webdev, javascript, beginners, react
Published: 2023-05-23 20:59:02

---
title: "Create Tik-Tok/Youtube Shorts like snap infinite scroll - React"
datePublished: Tue May 23 2023 13:36:49 GMT+0000 (Coordinated Universal Time)
cuid: cli0bljy8000b09l62g5qfwsx
slug: create-tik-tokyoutube-shorts-like-snap-infinite-scroll-react
cover: https://cdn.hashnode.com/res/hashnode/image/upload/v1681106230666/ed1b978f-93ee-4b66-847d-808d8fa9f140.jpeg
ogImage: https://cdn.hashnode.com/res/hashnode/image/upload/v1681106189200/6af2f69b-80d8-47af-85c0-deab8fb9c844.jpeg


---

Scroll snap is when you scroll a little and it auto scrolls to the next card in the list. You must have seen this feature on Instagram, youtube shorts and TikTok.

Snap Scroll can be achieved by CSS only. [https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-type](https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-type)

![gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1681106250162/6826312c-5c01-4397-99e8-b1d0e812a483.gif)

There are three ways to achieve this effect in react

1. Vanilla CSS
    
2. Styled Components
    
3. React-hook
    

Browser Compatibility of `scroll-snap-type` is great. All the browsers have stable support for it.

![browser util](https://cdn.hashnode.com/res/hashnode/image/upload/v1684824195605/d4cad969-86ae-493a-8673-f7e8cdb9273e.png)

> Before you can define scroll snapping, you need to enable scrolling on a scroll container. You can do this by ensuring that the scroll container has a defined size and that it has [`overflow`](https://developer.mozilla.org/en-US/docs/Web/CSS/overflow) enabled.

You can then define scroll snapping on the scroll container by using the following two key properties:

* [`scroll-snap-type`](https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-type): Using this property, you can define whether or not the scrollable viewport can be snapped to, whether snapping is required or optional, and the axis on which the snapping should occur.
    
* [`scroll-snap-align`](https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-align): This property is set on every child of the scroll container and you can use it to define each child's snap position or lack thereof.
    
    * (Using the [`scroll-snap-stop`](https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-stop) property, you can ensure that a child is snapped to during scrolling and not passed over.
        
    * [`scroll-margin`](https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-margin) properties on child elements that are snapped to during scrolling to create an outset from the defined box.) \[Optional\]
        
    * Optional [`scroll-padding`](https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-padding) properties can be set on the scroll container to create a snapping offset. \[Optional\]
        

### Vanilla CSS

The `scroll-snap-type` [CSS](https://developer.mozilla.org/en-US/docs/Web/CSS) property sets how strictly snap points are enforced on the scroll container if there is one.

For the snap-scroll to work properly, we will have a Container and Children. the container will have the `scroll-snap-type` CSS property and the Children will have the `scroll-snap-align` CSS property.

```css
scroll-snap-type: x mandatory;
scroll-snap-type: y proximity;
```

x and y will constrain the scroll-snap action to the x-axis and y-axis.

**Mandatory** means the scroll will always rest on the scroll-snap point( video component ).

**Proximity** is based on the proximity of the scroll-snap point/Children. Scroll container **may** come to rest on a snap point if it isn't currently scrolled considering the user agent's scroll parameters

Let's start with simple HTML structure.

```xml

<!-- Scroll Container Component  -->
<div class="container" dir="ltr">
  <!-- List Component  -->
  <div id="list">
  </div>
  <!-- Loader Component  -->
  <p id="watch_end_of_document"  class="loader">
  Loader ...
</p>
</div>
```

```css
.container {
   height: 100vh;
   scroll-snap-type: y mandatory;
   overflow: scroll;
}


.item {
  margin: 0;
  padding: 20px 0;
  text-align: center;
  scroll-snap-align: start;
  height: 300px;
}

.loader {
  height: 50px;
  display: flex;
  background: #eee;
  justify-content: center;
}


.item:nth-child(even) {
  background: #eee;
}
```

#### How does infinite scroll work?

Whenever the loader component comes into view, we run a fetch function.

```javascript
// Get the Loader Component
const loader = document.getElementById("loader")

// addItems - can also be fetch function
function addItems() {
  const fragment = document.createDocumentFragment();

  for (let i = index + 1; i <= index + count; ++i) {
    const item = document.createElement("p");

    item.classList.add("item");
    item.textContent = `#${i}`;

    fragment.appendChild(item);
  }
  document.getElementById("list").appendChild(fragment);
  index += count;
}

// Using the IntersectionObserver API we will observe the loader component 
// if the 
const io = new IntersectionObserver(entries => {
  entries.forEach(entry => {
    // componet is not in view we do nothing 
    if (!entry.isIntersecting) {
      return;
    }
    console.log('this is working');
    addItems();
  });
});

io.observe(loader);
```

{% codepen https://codepen.io/pratiksharma15/pen/abRQXKj %}



### Styled Components

Let's use Styled Components in React to do the same. Starting with the list Container Component which would have `scroll-snap-type` and `overflow-y` .

```javascript
import styled from "styled-components";

const List = styled.div`
  max-height: 100vh;
  overflow-y: scroll;
  scroll-snap-type: y mandatory;
  background: #fff;
  display: flex;
  flex-direction: column;
  gap: 20px;
  scrollbar-width: none;

  &::-webkit-scrollbar {
    display: none; /* for Chrome, Safari, and Opera */
  }
`;
```

I used `&::-webkit-scrollbar` to hide the scrollbar.

Now, the Item component would have the `scroll-snap-align` CSS property.

```javascript
const Item = styled.div`
  margin: 0;
  padding: 20px 0px;
  text-align: center;
  display: flex;
  justify-content: center;
  align-items: center;
  align-content: center;
  scroll-snap-align: start;
  min-height: 70vh;

  background: #eee;
`;
```

Now, to we are done with the scroll-snap.

#### Infinite scroll with styled-component

1. Create a Loader Component
    
2. We are going to use the `reack-hook-inview` to observe if the loader is in the viewport
    
3. If the Loader is in the viewport, fetch more items.
    

```javascript
const Loader = styled.div`
  min-height: 20vh;
  margin-bottom: 30px;
  display: flex;
  background: #444;
  scroll-snap-align: start;
  color: #eee;
  align-content: center;
  align-items: center;
  scroll-snap-align: start;

  justify-content: center;
`;
```

```javascript
function ScrollContainer() {
  const [state, setState] = useState([1, 2, 3, 4, 5]);
  return (
    <List>
      {state.map((el, index) => (
        <Item key={index + el}>{el} </Item>
      ))}
      <Loader >Loading...</Loader>
    </List>
  );
}
export default ScrollContainer;
```

`react-hook-inview` provides `useInView` hook, which we can use to observe the loader component.

We are going to use `useEffect` , if the Loader is in the viewport. We Fetch more data.

```javascript
function ScrollContainer() {
... 

const [ref, isVisible] = useInView({
    threshold: 1
  });

  const newData = [...Array(10).keys()].map((x) => x + state.length + 1);

  useEffect(() => {
    if (isVisible) {
      setState((state) => [...state, ...newData]);
    }
  }, [isVisible]);

  return (
    <List>
      {state.map((el, index) => (
        <Item key={index + el}>{el} </Item>
      ))}
      <Loader ref={ref}>Loading...</Loader>
    </List>
  );
}
```

That's it 🔥 . Here is CodeSandbox with all the code

{% codesandbox https://codesandbox.io/embed/infinite-scroll-snap-with-styled-components-nnmnom?fontsize=14&hidenavigation=1&theme=dark %}



### React Hooks

We are going to use [react-use-scroll-snap](https://www.npmjs.com/package/react-use-scroll-snap?activeTab=readme) . `react-use-scroll-snap` Give us simple API and keyboard accessibility as well. It uses the tweezer.js library.

```javascript
import useScrollSnap from "react-use-scroll-snap";
import { useRef } from "react";

function ScrollComponent() {
  const scrollRef = useRef(null);
  const data = Array(10)
        .fill(1)
        .map((i, e) => e + 1);

  useScrollSnap({ ref: scrollRef, duration: 100, delay: 50 });
 

  return (
    <section className="container" ref={scrollRef}>
      {data.map((el, index) => (
        <div key={el} className="item">
          {el}
        </div>
      ))}
    </section>
  );
}
```

See, less code 🙌🏻. Here is the code Sandbox for you to fork it. Try to add a loader component, add a fetch function and `useEffect` to call the fetch function.

{% codesandbox https://codesandbox.io/embed/react-use-scroll-snap-example-f3ndfw?fontsize=14&hidenavigation=1&theme=dark %}

#### Further Improvement:

1. Add Auto-play with [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)
    

#### Reference:

1. [https://developer.mozilla.org/en-US/docs/Web/CSS/CSS\_Scroll\_Snap/Basic\_concepts](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Scroll_Snap/Basic_concepts)
    
2. [https://developer.mozilla.org/en-US/docs/Web/CSS/CSS\_Scroll\_Snap](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Scroll_Snap)
    
3. [https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-type#try\_it](https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-type#try_it)