¿Cómo crear un Carrusel con Next y Chakra UI?
Tags: espanol, nextjs, embla, chakraui
Published: 2022-05-24 02:06:49

En este post, vamos a desarrollar un Carrusel utilizando la librería [Embla Carousel](https://www.embla-carousel.com/), el framework [Next.js](https://nextjs.org/) y la librería [Chakra UI](https://chakra-ui.com/) - esta última, al igual que Next.js, desarrollada por [Vercel](https://vercel.com/) y la comunidad Open Source.

A lo largo del post, aprenderemos:

* Por qué elegir Embla Carousel
* Cómo integrar Embla Carousel a tu proyecto en Next.js y Chakra UI
* Cómo agregar paginación a tu Carousel

Si querés ir directamente al **repositorio en GitHub** puedes hacerlo desde [aquí](https://github.com/ggchicote/nextjs-chakra-embla-carousel)

---

### ¿Qué es Embla Carousel?

Embla Carousel es una **librería agnóstica** que, como su nombre lo indica, facilita la construcción de carruseles. Su uso garantiza fluidez y precisión tanto en el movimiento como deslizamiento de los elementos (slides) que componen al carrusel. Es una librería _dependency-free_ y liviana, por lo que su instalación es rápida y posee una baja dependencia de otras librerías para su funcionamiento.

De muy **fácil integración a tu proyecto**, con una [_API_](https://www.embla-carousel.com/api/) muy bien documentada y una extensa lista de _plugins_ que permiten potenciar las funciones de tu componente carrusel. 

Por último, Embla Carousel funciona en todos los navegadores modernos, incluido **IE11**.

---

### Configurar nuestro proyecto

Crea un proyecto nuevo utilizando el comando `create-next-app`:

```bash
npx create-next-app my-awesome-embla-carousel
```

Una vez creado el proyecto y dentro del mismo, vamos a continuar instalando las dependencias necesarias para integrar **Chakra UI**. Según la [documentación oficial](https://chakra-ui.com/guides/getting-started/nextjs-guide):

```bash
npm i @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^6
# or
yarn add @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^6
```

Por último, pero no menos importante, vamos a agregar a nuestro proyecto la versión de la librería **Embla Carousel para React**:

```bash
npm install embla-carousel-react --save
```
Hasta el momento, tu archivo `package.json` debería verse de la siguiente manera:

![Listado de dependencias en el archivo package.json](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hbs1oj6f740k8voenag1.png)
 
Recuerda que las **versiones de las dependencias pueden variar**. Al momento de escribir este post, la versión de la librería Embla Carousel React es la _6.2.0_

---

### Estructura básica del componente Carousel

Para su integración con React, Embla Carousel provee el _hook_ `useEmblaCarousel`. El mismo devuelve dos parámetros, la **referencia al carrusel** que luego será utilizada en la composición visual de nuestro componente y la **API** que nos dará acceso a todos los métodos disponibles.

```js
import useEmblaCarousel from 'embla-carousel-react'

const [emblaRef, emblaApi] = useEmblaCarousel()
```

A su vez, `useEmblaCarousel` permite la asignación de [opciones globales](https://www.embla-carousel.com/api/options/#global-options) a nuestro carrusel. A modo de ejemplo:

```js
  const [emblaRef, embla] = useEmblaCarousel({

    // alinea el primer slide al principio del viewport,
    // caso contrario, por defecto, lo hará en el medio
    align: "start",

    // si el carrusel llegá al final retorna 
    // automáticamente al primer slide
    loop: true,

    // determina si el usuario puede desplazar los slides
    // a través del mouse u otra interacción. Por defecto: true
    draggable: false,

    // determina la velocidad de desplazamiento entre slides.
    speed: 15,

    // ... más opciones globales
  });
```

* _la asignación de **opciones globales** debe realizarse por única vez y hacerse antes de inicializar el carrusel. Caso contrario, re asignar opciones globales puede producir efectos inesperados en el comportamiento del componente carrusel._

Para una lista detallada de las opciones globales posibles, consultar [aquí](https://www.embla-carousel.com/api/options/#option-priority).

Por otro lado, la **composición visual de nuestro carrusel** va a requerir de una estructura básica de **tres elementos** - o sub componentes:

#### Viewport
* El _hook_ nos da una referencia para asociar al elemento envolvente - padre -, necesario para manipular el excedente o desbordamiento con la propiedad [_overflow:hidden_](https://developer.mozilla.org/es/docs/Web/CSS/overflow)

#### Container
* Un contenedor que encerrará todos los _slides_ del carrusel bajo la propiedad [_flex-row_](https://developer.mozilla.org/es/docs/Web/CSS/flex-direction)

#### Slides
* Tendrá un listado con cada _slide_ de nuestro carrusel

---

### Comencemos a programar

Hasta ahora:

* Aprendimos qué es Embla Carousel
* Instalamos un proyecto Next.js
* Limpiamos la estructura de nuestro proyecto
* Agregamos las dependencias necesarias de Embla Carousel y Chakra UI

¡Es hora de empezar a trabajar con código! Vamos a construir un **carrusel con repositorios de proyectos** bajo distintas tecnologías y programadores. La información para completar cada slide - imagen, desarrollador, titulo, indicadores, descripción, ect - puede ser consumida de forma local o directamente desde una API. En nuestro caso, usaremos un  **archivo local** `repositories.js` al proyecto que provee un arreglo de objetos con toda la información necesaria. Para la navegación entre los _slides_ usaremos de ejemplo una **paginación de puntos** (_dot's type pagination_). 


![Prevista del proyecto carrusel terminado](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gvspcc73eo63wfgbi70b.png)

Nuestro archivo `index.jsx` debería verse de la siguiente manera:

```js
import { ChakraProvider } from '@chakra-ui/react'
import useEmblaCarousel from 'embla-carousel-react'
import { useEffect, useCallback, useState } from 'react'
import { Container, Flex, Heading, Button, Box } from '@chakra-ui/react'
import SlideImage from '../components/SlideImage'
import SlideDetails from '../components/SlideDetails'
import repositories from '../../repositories'

const EmblaCarousel = () => {
  const [emblaRef, emblaApi] = useEmblaCarousel({ loop: false })
  const [scrollSnaps, setScrollSnaps] = useState([]);
  const [selectedIndex, setSelectedIndex] = useState(0);

  /* 
   * función que a través de la API de Embla Carousel
   * y el método scrollTo permite navegar al índice
   * correspondiente al "dot" de la navegación
   */
  const scrollTo = useCallback(
    (index) => emblaApi && emblaApi.scrollTo(index),
    [emblaApi]
  );

  /*
   * función que actualiza el estado del índice
   * correspondiente al slide actual  
   */
  const onSelect = useCallback(() => {
    if (!emblaApi) return;
    setSelectedIndex(emblaApi.selectedScrollSnap);
  }, [emblaApi, setSelectedIndex]);

  /*
   * por cada re-renderizado del componente nos aseguramos que
   * la librería Embla esté "montada" correctamente
   */
  useEffect(() => {
    if (!emblaApi) return;
    onSelect();
    setScrollSnaps(emblaApi.scrollSnapList);
    emblaApi.on("select", onSelect);
  }, [emblaApi, setScrollSnaps, onSelect]);

  return (
    {/* código de renderizado detallado abajo */}
  )
}

export default EmblaCarousel
```

Seguimos sumando código a nuestro archivo `index.jsx`, ahora el encargado de renderiza el elemento Carrusel:

```js
return (
    <ChakraProvider>
      <Box bg="#030c1b" minHeight="100vh">

        <Container maxW="100%" py={10} >
          <Heading textAlign="center" color="white">
            My awesome Embla Carousel using + NextJS + Chakra
          </Heading>
        </Container>

        {/* Viewport */}
        <Box overflow="hidden" maxW="100%" p={0} ref={emblaRef} >
          {/* Container */}
          <Flex h='auto' py={[0, 5, 5]}>
            {/* Slides */}
            {repositories.map(repo => {
              return (
                <Flex key={repo.id} direction={{ base: 'column', md: 'row' }} justifyContent="center" position="relative" flex="0 0 100%">
                  <SlideImage imageSrc={repo.imgSrc} />
                  <SlideDetails item={repo} />
                </Flex>
              )
            })}
          </Flex>
        </Box>
      </Box>
    </ChakraProvider>
)
```

Considere que los componentes `SlideImage` y `SlideDetails` tienen el objetivo de agregarle sentido en términos tanto de diseño como de contenido al carrrusel. Si lo desean podrán descargarlos directamente desde [acá](https://github.com/ggchicote/nextjs-chakra-embla-carousel/tree/main/components), sino, podrán crear su propio contenido y diseño.

Las imágenes utilizadas, pueden descargarse también del repositorio desde [aquí](https://github.com/ggchicote/nextjs-chakra-embla-carousel/tree/main/public/images). Las mismas están ubicadas en la carpeta `public/images` de Next.js, [destinada para servir archivos estáticos](https://nextjs.org/docs/basic-features/static-file-serving).  

Hasta ahora, le hemos dado estilo a nuestro componente carrusel de repositorios, donde cada _slide_ posee una imagen a la izquierda y detalles del repositorio a la derecha. Además, el componente ocupará el 100% de la _viewport_ por lo que se mostrará de a un _slide_ a la vez.

Por último, nos queda agregar el **sistema de paginación de puntos**. El mismo nos da una idea de la cantidad de elementos - slides - que posee el carrusel y a la vez provee una herramienta práctica para desplazarse entre cada _slide_.

Agregamos el siguiente código al final de nuestro archivo `index.jsx`, dentro del provedor de contexto de Chakra UI(`ChakraProvider`):

```js
return (
    <ChakraProvider>

        {/* código correspondiente al elemento carrusel */}

        <Container maxW="container.xl" py={10}>
        <Flex justifyContent="center" alignItems="center" mt={5}>
            {scrollSnaps.map((_, idx) => (
            <Button w={2} h={2} mx={1} size="3" bg={idx === selectedIndex ? "yellow.500" : "gray.300"} key={idx} 
            onClick={() => scrollTo(idx)}/>
            ))}
        </Flex>
        </Container>
    </ChakraProvider>
)
```
Terminamos!! Recordá que **cualquier duda o comentario siempre serán bienvenidos!**

Podés descargarte el repositorio del proyecto completo desde [aquí](https://github.com/ggchicote/nextjs-chakra-embla-carousel).

---

### Conclusiones

Hemos aprendido a crear fácilmente un carrusel con la ayuda de la librería [Embla Carousel](https://www.embla-carousel.com/) e integrarlo a nuestro proyecto de [Next.js](https://nextjs.org/) y [Chakra UI](https://chakra-ui.com/).

Embla Carousel es muy simple y te permite crear elementos con tu propio estilo según tus necesidades.

##### Hasta la próxima!!!

---

### Links de interes

[Embla Carousel](https://www.embla-carousel.com/)
[React Embla Carousel](https://www.embla-carousel.com/get-started/react/)
[Embla API](https://www.embla-carousel.com/api/)
[Next.js](https://nextjs.org/)
[Chakra UI](https://chakra-ui.com/)