Implementing pull-to-refresh in React with Tailwind CSS
Tags: react, css, webdev
Published: 2022-12-08 21:37:14

**Written by [Emmanuel Odioko](https://blog.logrocket.com/author/emmanuelodioko/)✏️**

Many of us have probably gotten sucked into particularly captivating videos and posts on social media and don’t want to stop scrolling. When the media algorithm starts recommending less fascinating posts, your best option is likely to swipe to pull down the screen, which initiates a gesture that retrieves new suggested data or posts. If this doesn't capture your attention, you may repeat the process in some cases until the algorithm meets your satisfaction. 

The gesture that initiates the retrieval of your next entertaining post is pull-to-refresh, which we will be discussing throughout the article. 

_Jump ahead:_

*   [What is pull-to-refresh?](#what-is-pull-to-refresh)
    *   [Benefits of pull-to-refresh](#pull-to-refresh-benefits)
*   [What is overscroll behavior?](#what-is-overscroll-behavior)
*   [Project overview](#project-overview)
    *   [Prerequisites](#prerequisites)
*   [Setting up our development area](#setting-up-development-area)
*   [Implementing pull-to-refresh with React](#implementing-pull-to-refresh-react)
    *   [Setting the `startPoint` and `pullChange`](#setting-startpoint-pullchange)
    *   [Forcing a refresh](#forcing-refresh)
*   [Styling with Tailwind CSS and writing logic](#styling-with-tailwind-css-writing-logic)

## What is pull-to-refresh? <a name="what-is-pull-to-refresh">

When the user drags from the top of the page, a loader should appear and should only be visible a bit below the top screen at this point. This entire action indicates to the user that new data is being retrieved. 

When the user lets go of the drag triggered by their touch, the loader disappears and at the same time, new data should appear in the feed. At the end of the article, we should have something resembling the GIF below: ![The Demo App Showcasing a Pull-to-refresh Feature](https://blog.logrocket.com/wp-content/uploads/2022/11/demo-app-pull-to-refresh.gif) 

This action is carried out by dragging the page's top, and if there is a fresh update, it displays just immediately. Swiping or pulling makes it easy to retrieve info; this action was created by Loren Brichter in the Tweetie app in 2008, and it quickly became popular like anything excellent would.

### Benefits of pull-to-refresh <a name="pull-to-refresh-benefits">

Numerous smartphone apps use the pull-to-refresh gesture, which isn’t relatively new. They do so for the following reasons:

*   This action reduces screen clutter and frees up space
*   It provides room to view fresh data from the server

## What is overscroll behavior?

The [`overscroll` CSS attribute](https://developer.mozilla.org/en-US/docs/Web/CSS/overscroll-behavior#:~:text=The%20overscroll-behavior%20CSS%20property,and%20overscroll-behavior-y%20) determines what a browser does when it approaches either the bottom or the top edge of a scrolling zone. By default, the mobile browser tends to refresh a page when the top of the page is reached. In most cases, as a developer intending to implement a custom pull-to-refresh, this default behavior is not desirable, so you can employ the use of overscroll behaviors to override the browser's default pull-to-refresh gesture. 

The code below is a general implementation of overscroll behavior from the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/CSS/overscroll-behavior):

```css
/* Keyword values */
overscroll-behavior: auto; /* default */
overscroll-behavior: contain;
overscroll-behavior: none;

/* Two values */
overscroll-behavior: auto contain;

/* Global values */
overscroll-behavior: inherit;
overscroll-behavior: initial;
overscroll-behavior: revert;
overscroll-behavior: revert-layer;
overscroll-behavior: unset;
```

To override the browser's inbuilt pull-to-refresh gesture in our quest to build our own, we'll use the `overscroll-behavior: contain`; property. However, because we are aiming for the vertical edge of our scrolling region, the `overscroll-behavior` in our case will be `overscroll-behavior-y: contain`. 

To use this, we will give the universal selector the `overscroll-behavior-y: contain` property in our `index.css` file, and that will be all:

```css
* {
  overscroll-behavior-y: contain;
}
```

## Project overview

The reason we invest a lot of time in development is to ensure that our end users are completely satisfied. The pull-to-refresh gesture is intended to remain consistent throughout. Although there may be a demand for uniqueness in the animation and SVG style, the pull-to-refresh gesture itself should allow users to refresh whenever they feel the need to do so. 

This article will focus on the implementation of custom pull-to-refresh gestures with React and overscroll behavior in CSS. The custom pull-to-refresh gesture should be something that appears at the top of our mobile browser and should only be visible when the user scrolls past the page borders.

### Prerequisites <a name="prerequisites">

You must satisfy these requirements to follow this article properly:

*   A copy of [Node.js](https://nodejs.org/en/) installed
*   Fundamental knowledge of JavaScript syntax
*   A working grasp of [React](https://reactjs.org/) and React Hooks
*   Familiarity with the CLI and its commands
*   Familiarity with [Tailwind CSS](https://tailwindcss.com/)

## Setting up our development area <a name="setting-up-development-area">

To get our development area up and running, open up your terminal and run the commands:

```shell
cd Desktop
npx create-react-app pull-to-refresh
npm start
```

To install Tailwind, [follow these steps](https://v1.tailwindcss.com/docs/installation) from the documentation. At this point, our application should be up and running, and you should clear the `app.js` file for a clean start.

## Implementing pull-to-refresh with React <a name="implementing-pull-to-refresh-react">

To be able to implement the pull-to-refresh gesture, we must first acknowledge that this gesture will not be complete until there is first a touch; then a swipe downward, which is the drag; then an end to the drag. These actions are implemented with what we’ll refer to as [`EventListeners`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener), which will be called whenever an action is delivered to the browser window. 

There are a couple of different `EventListeners`, but only three will get the job done. Those three are:

1.  `touchstart`
2.  `touchmove`
3.  `touchend`

`touchstart` is only going to trigger when it senses the first touch. `touchmove` will trigger when the touch is followed by a move or drag. Lastly, `touchend` will trigger when there are no more touches registered in the window/on screen. 

The `useEffect` Hook will be responsible for adding and removing our event listeners. After importing `useEffect()`, we can copy and paste the code below into our `App.js` file:

```javascript
// add and remove event listeners
  useEffect(() => {
    window.addEventListener("touchstart", pullStart);
    window.addEventListener("touchmove", pull);
    window.addEventListener("touchend", endPull);
    return () => {
      window.removeEventListener("touchstart", pullStart);
      window.removeEventListener("touchmove", pull);
      window.removeEventListener("touchend", endPull);
    };
  });
```

Since we are dealing with a pull gesture, there will inevitably be a touch to trigger the pull gesture. Therefore, our first concern is to have a state to store the user’s first touch towards the top of the screen.

### Setting the `startPoint` and `pullChange` <a name="setting-startpoint-pullchange">

First things first, we will set the `startPoint`, which is a state to hold the `startPoint`’s [`screenY`](https://developer.mozilla.org/en-US/docs/Web/API/Window/screenY) value when the user’s initial touch is registered.

To create the state, we will need to first import `useState()` and declare a state using the Hook.

```javascript
  /**
    state to hold the start point
  */
    const [startPoint, setStartPoint] = useState(0);
```

The next thing we'll do is create a state that will calculate how far the user has pulled the screen down — this will be the state that holds the change from the start point to the current point. The pull change will be equivalent to `current point - start point`; hence, we will refer to this state as the `pullChange`. The current point is where the user has been able to drag from the `startPoint`.

```javascript
/**
   * 
    state to hold the change in the start point and current point
    the pull change is equivalent to current point - start point
  */
  const [pullChange, setPullChange] = useState();
```

We will then need a `ref` to target the `.Refresh-container` element in the DOM. We can implement this by first [importing `useRef()` from React](https://blog.logrocket.com/complete-guide-react-refs/) and declaring it, as seen below:

```javascript
 /**
    ref to target the `.refresh-container` element in the DOM
   */
    const refreshCont = useRef(0);
```

### Forcing a refresh <a name="forcing-refresh">

We also need a function to initialize loading and force the page to refresh within a second. This is the right time to create the function called `initLoading`, which adds the `.loading` class to the refresh container element to signify the loading state. We’ll also need a `setTimeout` function, which will reload the page after 1000ms.

```javascript
const initLoading = () => {
  refreshCont.current.classList.add("loading");
  setTimeout(() => {
    window.location.reload();
  }, 1000);
};
```

Now, we need a function to listen to the start point state and handle our `touchstart` event. We will call this function `pullStart`. This function gets the start point of the user touch gesture and the pull start, which only runs the first time you touch the screen — so the function is very useful for getting the start position.

```javascript
const pullStart = (e) => {
  const { screenY } = e.targetTouches[0];
  setStartPoint(screenY);
};
```

As seen in the `useEffect()` Hook above, the pull function will run when your finger moves on the screen. As you move from the top down to the middle of the screen, it calculates the difference between the current position and the starting position and saves it to our `pullChange` function.

```javascript
const pull = (e) => {
  /**
   * get the current user touch event data
   */
  const touch = e.targetTouches[0];
  /**
   * get the touch position on the screen's Y axis
   */
  const { screenY } = touch;
  /**
   * The length of the pull
   *
   * if the start touch position is lesser than the current touch position, calculate the difference, which gives the `pullLength`
   *
   * This tells us how much the user has pulled
   */
  let pullLength = startPoint < screenY ? Math.abs(screenY - startPoint) : 0;
  setPullChange(pullLength);
  console.log({ screenY, startPoint, pullLength, pullChange });
};
```

As seen in `useEffect` above, `endPull` is a function that runs at the end of the touch gesture.

```javascript
, endPull = (e) => {
  setStartPoint(0);
  setPullChange(0);
  if (pullChange > 220) initLoading();
};

// function to reset the refresh button and start the loading by running the `initLoading()` function when `pullChange` has passed a certain threshold
```

## Styling with Tailwind CSS and writing logic <a name="styling-with-tailwind-css-writing-logic">

Below, we styled the DOM with Tailwind. We have our ref in the `div` container, our `margin-top` will be assigned to `pullChange()`, and it is divided by an arbitrary value, which makes the drag slower than normal. 

The `margin-top` is responsible for pushing our refresh container down. In our SVG, we also have our spinner. After styling our spinner, we see that the `rotate` function goes along with `pullChange`: the spinner rotates as it is being pulled, and by the time it is released, it will add the `.loading` class.

```javascript
//Before Tailwind
 <div
        ref={refreshCont}
        className=""
        style={{ marginTop: pullChange / 3.118 || "" }}
      >
        <div className="">
          <svg
            xmlns="http://www.w3.org/2000/svg"
            fill="none"
            viewBox="0 0 24 24"
            strokeWidth={1.5}
            stroke="currentColor"
            className=""
            style={{ transform: `rotate(${pullChange}deg)` }}
          >
            <path
              strokeLinecap="round"
              strokeLinejoin="round"
              d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"
            />
          </svg>
        </div>
      </div>
      <div className="">
        <header className="">
          <h1 className="">Welcome to my app!</h1>
          <p>Pull down to refresh</p>
        </header>
      </div>
```

![Spinner Rotating After Pull-to-refresh](https://blog.logrocket.com/wp-content/uploads/2022/11/pull-to-refresh-spinner-tailwind.gif)

```javascript
//After Tailwind
<div
ref={refreshCont}
className="refresh-container w-fit -mt-10 m-auto"
style={{ marginTop: pullChange / 3.118 || "" }}
>
<div className="refresh-icon p-2 rounded-full">
  <svg
    xmlns="http://www.w3.org/2000/svg"
    fill="none"
    viewBox="0 0 24 24"
    strokeWidth={1.5}
    stroke="currentColor"
    className="w-6 h-6"
    style={{ transform: `rotate(${pullChange}deg)` }}
  >
    <path
      strokeLinecap="round"
      strokeLinejoin="round"
      d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"
    />
  </svg>
</div>
</div>
<div className="body flex justify-center items-center min-h-screen">
<header className="flex flex-col text-center">
  <h1 className="text-4xl font-bold">Welcome to my app!</h1>
  <p>Pull down to refresh</p>
</header>
</div>
```

If every step was carefully and consciously followed, we should arrive at a result that looks just like the GIF below: ![The Demo App Showcasing a Pull-to-refresh Feature](https://blog.logrocket.com/wp-content/uploads/2022/11/demo-app-pull-to-refresh.gif)

## Conclusion

The gesture is crucial because it adds to the long list of features that let users interact fully with an application. In this tutorial, we went over how to implement pull-to-refresh step by step; it was a quick, seamless process that resulted in a job well done. I'd like to take this opportunity to express my gratitude for sticking with me this far.

---
##Cut through the noise of traditional React error reporting with LogRocket

[LogRocket](https://lp.logrocket.com/blg/react-signup-issue-free) is a React analytics solution that shields you from the hundreds of false-positive errors alerts to just a few truly important items. LogRocket tells you the most impactful bugs and UX issues actually impacting users in your React applications.

[![LogRocket signup](https://blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png)](https://lp.logrocket.com/blg/signup)

[LogRocket](https://lp.logrocket.com/blg/react-signup-issue-free) automatically aggregates client side errors, React error boundaries, Redux state, slow component load times, JS exceptions, frontend performance metrics, and user interactions. Then LogRocket uses machine learning to notify you of the most impactful problems affecting the most users and provides the context you need to fix it.

Focus on the React bugs that matter — [try LogRocket today](https://lp.logrocket.com/blg/react-signup-issue-free).