Building a pager component from scratch*
Tags: react
Published: 2019-07-23 23:21:13

In this tutorial we'll go through the steps of building a pager component from "scratch" (not entirely accurate -- we'll use a few dependencies to help with handling gestures and animations later on). This first article will capture the core behaviour of the pager component before moving onto things like animations and gestures.

I originally implemented this in react-native, so an example of what the final product will (sort of) look like can be found here: https://github.com/ajsmth/react-native-pager-component#readme


**Note:** I know that components for this kind of thing already exist -- this was made for my own learning, and hopefully yours too.


Before getting into the code, let's think about what we want to do...

We want a component that, when given a certain index, moves the matching child index into focus. So we'll be thinking in terms of indexes -- when the `activeIndex` prop changes, we'll want to shift the focus to the child that matches that index: 

`onChange(activeIndex) -> updateTranslation(activeIndex)`

We can achieve this by laying out all of our children horizontally inside a container view, like this: 

```
const absoluteFill = {
  position: "absolute",
  left: 0,
  right: 0,
  bottom: 0,
  top: 0
};

function Pager({ children, activeIndex, size }) {
  return (
    <div
      style={{
        display: "flex",
        alignSelf: "center",

        position: "relative",
        width: size,
        height: size,
        border: "2px solid green"
      }}
    >
      <div
        style={{
          ...absoluteFill
          // we will translate this container view
        }}
      >
        {React.Children.map(children, (element, index) => (
          <PageView index={index} width={size}>
            {element}
          </PageView>
        ))}
      </div>
    </div>
  );
}

function PageView({ children, index }) {
  // position each page side-by-side based on it's index
  const position = `translateX(calc(${100 * index}%))`;

  return (
    <div
      style={{
        ...absoluteFill,
        transform: position,
        border: "thin solid red"
      }}
    >
      {children}
    </div>
  );
}

```

Now all we have to do is update the translation value of the inner container based on the `activeIndex` prop:

```
function Pager({ children, activeIndex, size }) {
  // the total offset of the container div -- based on activeIndex
  const translateX = `translateX(${activeIndex * -100}%)`;

  return (
    <div ...>
      <div
        style={{
          ...absoluteFill,
          transform: translateX
        }}
      >
        {React.Children.map(children, (element, index) => (
          <PageView index={index} width={size}>
            {element}
          </PageView>
        ))}
      </div>
    </div>
  );
}
```

The inner container now shifts its focus by computing its offset from the `activeIndex` prop. This is a pretty basic implementation so far, but hopefully you can see that it captures the core behaviour of what the final component will do. 

The next steps will be to animate page transitions, and handle gestures to update the `activeIndex` based on user interaction. We'll also open up the component's API to make it fully controllable for use in other projects.

To see a full example visit https://codesandbox.io/s/modern-moon-o5etr

In the next article, we'll animate the transitions between pages: 

https://dev.to/ajsmth/building-a-pager-component-from-scratch-part-2-557l