19 septembre 2023
Découverte de Tamagui
9 minutes de lecture
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
etText
. - 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'écrirepaddingHorizontal
, on peut définir un shorthandpx
qui sera un alias pourpaddingHorizontal
.
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.