AccueilClientsExpertisesBlogOpen SourceJobsContact

19 septembre 2023

Découverte de Tamagui

9 minutes de lecture

Découverte de Tamagui

Dans l'univers react-native, nombreuses sont les librairies UI proposant du contenu intéressant. Cependant, parmi toutes ces librairies, il est parfois compliqué de lier la simplicité d'utilisation avec la performance. Je vous parlais dans un précédent article de native-base qui est très complet, que ce soit en terme de composants, mais aussi d'expérience développeur, cependant il présente des problèmes de performance trop importants pour que nous continuions à l'utiliser. Dans cette optique de migration de librairie, Tamagui s'est présenté comme un sérieux prétendant.

Tamagui, c'est quoi ?

Tamagui se veut être une librairie compatible pour toutes les plateformes pouvant faire tourner react-native, c'est-à-dire Android, iOS et les navigateurs web. Il est important de noter que, contrairement à la concurrence, Tamagui propose une compatibilité web. Celle-ci fonctionne tout d'abord grâce à react-native-web, mais aussi via un plugin Babel (facultatif) qui se charge de transformer les styles des composants Tamagui en CSS, incluant une compatibilité avec les media queries. Ce compilateur propose aussi une conversion des styles vers l'API StyleSheet de react-native. Il a donc pour but d'améliorer les performances afin de faire en sorte que la librairie fasse le moins de travail possible pour compiler les styles au runtime.

La librairie se divise en trois composantes principales :

  • Core : c'est la partie qui se charge de fournir les fonctionnalités de création de composants stylisés interagissant avec le thème de Tamagui. Cette partie fournit aussi deux composants de base: Stack et Text.
  • Static : le compilateur qui se charge de transformer les styles en CSS ou en StyleSheet
  • Tamagui : c'est la librairie qui contient les composants implémentés grâce aux fonctions de style de Core. On y retrouvera par exemple des boutons, des input, des modales, etc.

Installation

Il existe plusieurs manières d'installer Tamagui selon les besoins du projet :

  • Pour un projet visant une compatibilité web, il est possible d'utiliser le starter, qui fournit un monorepo incluant une application Expo utilisant Expo-Router ainsi qu'un site NextJS. Pour installer ce starter: npm create tamagui.
  • Pour un projet visant une compatibilité sur une seule plateforme, il est conseillé de passer quand même par un monorepo, étant donné que le compilateur se base sur un dossier devant se situer dans les node_modules. Cependant, pour une application mobile uniquement, il est possible de se passer du compilateur, car le gain de performance est minime. Pour installer la librairie UI : yarn add tamagui react-native-web react-dom. Pour installer le Core, sans la librairie UI : yarn add @tamagui/core react-native-web react-dom.

Pour la suite de cet article, nous utilisons le starter généré grâce à npm create tamagui.

Configuration

La configuration de Tamagui (que l'on appelle "thème" dans le cadre d'autres librairies) se divise en plusieurs catégories :

  • tokens : ce sont les différents alias associés aux couleurs, tailles, espacements, border radius, etc.
  • fonts : ce sont les différentes configurations de polices disponibles pour l'application.
  • themes : les thèmes sont un ensemble d'association clé <-> token. Ils permettent de définir un ensemble de styles pour une partie de l'application, ou pour toute l'application.
  • shorthands : ce sont des raccourcis permettant de définir des styles réutilisables dans l'application. Par exemple, au lieu d'écrire paddingHorizontal, on peut définir un shorthand px qui sera un alias pour paddingHorizontal.

Le starter propose déjà une configuration de base dans le fichier packages/ui/src/tamagui.config.ts, qu'il est possible de modifier selon les besoins du projet. Nous allons ici garder la configuration proposée. Cette configuration est référencée dans tous les packages et applications utilisant Tamagui. Pour le compilateur, celui-ci est configuré indépendamment pour le web (apps/next/next.config.js) et pour le mobile (apps/expo/babel.config.js).

Si vous choisissez de modifier la configuration, veillez à respecter les noms des tokens par rapport à la configuration par défaut si vous utiliser la librairie UI. En effet, cette dernière se base sur des noms de tokens précis. Cependant si vous utilisez seulement le Core, vous pouvez nommer les tokens comme vous le souhaitez.

Utilisation

Maintenant que tout est installé et configuré, nous allons pouvoir commencer à utiliser Tamagui. Lançons dans deux terminaux séparés les commandes yarn native et yarn web.

Supprimons le contenu de packages/app/features/home/screen.tsx pour n'y garder qu'un seul composant. C'est le composant rendu en premier sur notre application.

Utilisation basique

Affichons la forme la plus basique qui soit : un carré.

import { YStack } from '@my/ui'

export function HomeScreen() {
  return (
    <YStack f={1} ai="center" jc="center">
      <YStack w="$10" h="$10" bg="$blue12" />
    </YStack>
  )
}

Ici, on combine à la fois l'utilisation des shorthands (f -> flex, ai -> alignItems, etc.) et l'utilisation des tokens, préfixés d'un $. Selon la propriété, la valeur du token correspond à une partie des tokens de notre configuration. Par exemple dans le cas de la width (w), le token $10 correspond à la clé 10 de l'objet sizes des tokens de la configuration. Il est possible de faire référence à un token spécifique en préfixant la valeur par le type de token à utiliser. Ainsi, on pourrait transformer w="$10" en w="$space.10", ce qui ira récupérer la valeur de la clé 10 de l'objet space des tokens de la configuration.

Stylisation par interaction

Il est possible d'appliquer des styles différents selon les interactions qui se produisent sur le composant (hover, press, etc.). Pour cela, on peut utiliser les propriétés pressStyle, hoverStyle (web uniquement) et focusStyle (web uniquement). En web, les interactions sont gérées via les propriétés CSS natives (:hover, :focus, etc.).

import { YStack } from '@my/ui'

export function HomeScreen() {
  return (
    <YStack f={1} ai="center" jc="center">
      <YStack
        w="$10"
        h="$10"
        bg="$blue12"
        pressStyle={{
          bg: '$red12',
          br: '$2', // radius
        }}
        hoverStyle={{
          bg: '$orange12',
        }}
      />
    </YStack>
  )
}

Animations

Il est possible d'animer les styles d'un composant grâce à la prop animation. Sa valeur est basée sur la propriété animations de la configuration. Le starter nous propose des ensembles d'animations utilisant un driver basé sur l'API Animated de react-native, mais il est possible d'utiliser d'autres drivers, notamment un basé sur les animations CSS. Attention, selon le driver, certaines propriétés ne pourront être animées.

import { YStack } from '@my/ui'

export function HomeScreen() {
  return (
    <YStack f={1} ai="center" jc="center">
      <YStack
        w="$10"
        h="$10"
        bg="$blue12"
        br="$1"
        pressStyle={{
          bg: '$red12',
          br: '$10', // radius,
        }}
        hoverStyle={{
          bg: '$orange12',
        }}
        animation="bouncy"
      />
    </YStack>
  )
}

Utilisation des thèmes

Un thème est un ensemble de valeurs, associées à des couleurs en général, définies dans le cadre de toute l'application ou d'une partie de l'application. Il est possible de définir un thème pour une partie de l'application via la prop theme en y passant le nom du thème à utiliser. Pour référencer une valeur du thème, cela se passe de la même manière que pour les tokens, c'est-à-dire en préfixant le nom de la valeur avec un $.

import { YStack } from '@my/ui'

export function HomeScreen() {
  return (
    <YStack f={1} ai="center" jc="center" theme="blue">
      <YStack
        w="$10"
        h="$10"
        bg="$background"
        br="$1"
        pressStyle={{
          bg: '$backgroundPress',
          br: '$10', // radius,
        }}
        hoverStyle={{
          bg: '$backgroundHover',
        }}
        animation="bouncy"
      />
    </YStack>
  )
}

Ici, theme="blue" va faire en sorte que les propriétés background, backgroundPress et backgroundHover soient associées aux valeurs du thème blue. Tamagui propose aussi un concept de "sous-thème". Dans la configuration de base, nous pouvons retrouver par exemple un thème blue et blue_alt2. Le thème blue_alt2 est un sous-thème de blue. Il est ainsi possible de faire en sorte qu'un composant enfant utilisant le thème blue puisse appliquer le thème alt2. En interne, Tamagui effectue une concaténation entre le thème courant et thème parent avec un _, et utilise ce thème s'il existe.

import { YStack } from '@my/ui'

export function HomeScreen() {
  return (
    <YStack f={1} ai="center" jc="center" theme="blue">
      <YStack
        w="$10"
        h="$10"
        bg="$background"
        br="$1"
        pressStyle={{
          bg: '$backgroundPress',
          br: '$10', // radius,
        }}
        hoverStyle={{
          bg: '$backgroundHover',
        }}
        animation="bouncy"
      />
      <YStack
        w="$10"
        h="$10"
        bg="$background"
        br="$1"
        pressStyle={{
          bg: '$backgroundPress',
          br: '$10', // radius,
        }}
        hoverStyle={{
          bg: '$backgroundHover',
        }}
        animation="bouncy"
        theme="alt2"
      />
    </YStack>
  )
}

Variants

Tamagui propose un système de variants via sa fonction styled. A l'instantiation du composant, les variantes se présentent comme des props. Il existe trois notations possibles pour les variantes :

  • sous forme d'objet, ainsi une variante peut prendre un ensemble défini de valeurs
import { YStack, styled } from '@my/ui'

const MyStyledComponent = styled(YStack, {
  bg: '$blue6',
  variants: {
    shape: {
      circle: {
        br: 9999,
      },
      square: {
        aspectRatio: 1,
      },
    },
  },
})

export function HomeScreen() {
  return (
    <YStack f={1} ai="center" jc="center">
      <MyStyledComponent shape="circle" w="$10" h="$10" />
      <MyStyledComponent shape="square" w="$10" h="$10" />
    </YStack>
  )
}
  • sous forme de fonction associée à une valeur d'un token, ainsi une variante peut prendre un ensemble de valeurs définies par un type de token
import { YStack, styled } from '@my/ui'

const MyStyledComponent = styled(YStack, {
  bg: '$blue6',
  variants: {
    size: {
      '...size': (value, { tokens }) => ({
        width: tokens.size[value],
        height: tokens.size[value],
      }),
    },
  },
})

export function HomeScreen() {
  return (
    <YStack f={1} ai="center" jc="center">
      <MyStyledComponent size="$10" />
    </YStack>
  )
}
  • sous forme de fonction associée à un type de valeur
import { YStack, styled } from '@my/ui'

const MyStyledComponent = styled(YStack, {
  bg: '$blue6',
  variants: {
    size: {
      ':number': (value) => ({
        width: value,
        height: value,
      }),
    },
  },
})

export function HomeScreen() {
  return (
    <YStack f={1} ai="center" jc="center">
      <MyStyledComponent size={100} />
    </YStack>
  )
}

Il est bien entendu possible de faire référence à des valeurs du thème au sein des variants et la fonction styled, en plus de tous les autres types de tokens.

Media queries

Il est possible d'utiliser les media queries définies dans la configuration via les props. Pour cela, il faut préfixer le nom de la media query par un $.

import { YStack } from '@my/ui'

export function HomeScreen() {
  return (
    <YStack f={1} ai="center" jc="center">
      <YStack w="$10" h="$10" bg="$red6" $xs={{ bg: '$blue6' }} />
    </YStack>
  )
}

Dans la configuration par défaut, la media query xs possède un breakpoint maxWidth: 660. Ainsi, pour les écrans mobiles, le carré sera bleu, et pour les écrans plus larges, il sera rouge.

Extraction CSS

Côté web, il est possible de manière assez simple de vérifier si le CSS est correctement extrait. Dans un premier temps, dans le fichier apps/next/next.config.js, passer la valeur de disableExtraction à false, puis rafraîchir la page. En fouillant un peu dans le code source, on peut retrouver le CSS extrait dans le <head> de la page, ce qui n'est pas le cas si disableExtraction est à true.

En conclusion

Tamagui est à l'heure actuelle une librairie très prometteuse, proposant des fonctionnalités tout aussi intéressantes les unes que les autres. Il n'est pas nécessaire d'avoir une compatibilité Web pour utiliser Tamagui, et un usage uniquement focalisé sur une application mobile est totalement envisageable.

L'expérience développeur est de manière générale très positive, bien que la configuration puisse s'avérer un peu fastidieuse lors des premières utilisations de la librairie. De plus, certaines parties de la documentation sont encore à améliorer, même si, de manière générale, les parties importantes sont bien documentées.

Nous n'avons pas abordé certaines parties plus spécifiques de la librairie pour le theme builder ou encore le styled context, mais ces parties restent très intéressantes à explorer pour des usages avancés.

Chez Premier Octet, nous avons déjà adopté Tamagui dans le cadre de deux applications mobiles et les retours sont très positifs, donc nous allons continuer à nous en servir.

18 avenue Parmentier
75011 Paris
+33 1 43 57 39 11
hello@premieroctet.com

Suivez nos aventures

GitHub
Twitter
Flux RSS

Naviguez à vue