В какой-то момент я хотел решить несколько проблем с UI частью в приложении на React Native:
UI был слишком медленный. И здесь важно уточнить, что он был сделан с использованием styled-components.
В приложении не было возможности темизации, потому что токены импортировались как модуль в каждом компоненте. И как следствие, в приложении невозможно было сделать темную тему, потому что при смене токенов оно бы просто не перерендерилось.
Спойлер. Restyle решает обе проблемы, а еще дает дополнительные классные плюшки, о которых мы обязательно поговорим дальше в статье.
Restyle – что это?
The Restyle library provides a type-enforced system for building UI components in React Native with TypeScript. It's a library for building UI libraries, with themability as the core focus.
Restyle предоставляет набор инструментов, которые позволят строить UI продуктов на основе токенов. Особенно очень хорошо такой подход залетает, когда в продукте есть Дизайн Система и этот набор токенов действительно определен.
Одно из основных преимуществ библиотеки Restyle по сравнению с той же styled-system, в том, что она не использует styled-components под капотом. В основе Restyle используются абстрактные стили StyleSheet, поэтому ожидается более высокая производительность.
Немного про токены
Что мы понимаем под токенами? Это цвета, отступы, радиусы и так далее – все строгие ограничения, касающиеся стилей компонентов.
У нас будет несколько групп токенов:
Цвета
{ "error500": "rgba(224, 25, 53, 1)", "error300": "rgba(231, 78, 99, 1)", "error100": "rgba(255, 242, 244, 1)", "warning500": "rgba(255, 184, 0, 1)", "warning100": "rgba(255, 246, 221, 1)", "success500": "rgba(46, 181, 24, 1)", "success100": "rgba(234, 248, 232, 1)", "g100": "rgba(255, 255, 255, 1)", "g200": "rgba(244, 245, 248, 1)", "g300": "rgba(208, 211, 221, 1)", "g400": "rgba(164, 166, 178, 1)", "g500": "rgba(24, 24, 24, 1)", }
Отступы
{ "1": 4, "2": 8, "3": 12, "4": 16, "5": 20, "6": 24, "7": 28, "8": 32, "9": 36, "10": 40 }
Радиусы
{ "xxs": 4, "xs": 8, "s": 12, "m": 18, "l": 20, "xl": 24, "xxl": 32 }
Типографика
{ "headingXL": { "fontSize": 40, "lineHeight": 44, "letterSpacing": -0.4 }, "headingL": { "fontSize": 36, "lineHeight": 40, "letterSpacing": -0.36 }, "headingM": { "fontSize": 28, "lineHeight": 32, "letterSpacing": -0.28 }, "headingS": { "fontSize": 22, "lineHeight": 26, "letterSpacing": 0 }, "headingXS": { "fontSize": 17, "lineHeight": 22, "letterSpacing": 0 }, "bodyL": { "fontSize": 17, "lineHeight": 24, "letterSpacing": -0.34 }, "bodyM": { "fontSize": 15, "lineHeight": 22, "letterSpacing": -0.3 }, "bodyS": { "fontSize": 13, "lineHeight": 16, "letterSpacing": -0.26 } }
Брейкпойнты
{ "s": 0, "m": 360 }
А дальше эти ток��ны нужно расшарить на всё приложение, для этого библиотека Restyle предоставляет ThemeProvider:
import {ThemeProvider, createTheme} from '@shopify/restyle'; import { colors, spacing, breakpoints, textVariants, borderRadii } from './tokens'; import {Box,Text} from './'; const theme = createTheme({ colors, spacing, breakpoints, textVariants, borderRadii, }); export const App = () => { <ThemeProvider theme={theme}> <Box flex={1} background="g200" margin={2} > <Text variant="bodyM" color="g500">Привет!</Text> </Box> </ThemeProvider> }
Box & Text
Компоненты Box и Text являются основными строительными блоками для UI. Box создается на основе View и нужен для оберток, Text нужен для текстовых элементов соответственно. Они создаются один раз, и дальше их просто можно переиспользовать.
import {createBox, createText} from '@shopify/restyle'; export const Box = createBox(); export const Text = createText(); export const myComponent = () => { return ( <Box flex={1} background="g200" margin={2} > <Text variant="bodyM" color="g500">Привет!</Text> </Box> ) };
Text имеет свой собственный проп variant, где он как раз принимает значение токена типографики textVariant .
Стили прям в разметке
Нет необходимости выносить стили отдельно или создавать отельные файлы. Стили можно добавлять прям в компоненте, используя Restyle компоненты Box и Text.
Да, все правильно, мы будем писать стили прям в JSX. А почему это хорошо?
Не нужно создавать бесконечные названия блоков (Container, Wrapper и т.д)
Мы сразу видим всю картину целиком, за что отвечает каждый блок
Стилей не так уж и много обычно
Мы все равно часто добавляем стили в JSX, будет все однообразн��
Styled обертки сильно ухудшают производительность
Но все эти плюсы будут работать, если вы будете декомпозировать сложные компоненты на небольшие модули. В полотне верстки это будет выглядеть плохо.
Цвета
Цвета можно пробрасывать во все цветовые пропсы, используя ключи из темы (например g200, g300, g400).
Цветовые пропсы
backgroundColor [bg]
color
shadowColor
textShadowColor
borderColor
В квадратных скобках сокращенный вариант пропса
Посмотрим на пример, как это может использоваться:
<Box backgroundColor="g300" borderColor="g400" > <Text variant="bodyS" color="g200" > Any text </Text> </Box>
Отступы
Все отступы тоже можно пробрасывать прям в компоненты, используя специальные пропсы для этого. В нашем случае они добавляют отступы, кратные 4px.
Пропсы для отступов
margin [m], marginTop [mt], marginRight [mr], marginBottom [mb], marginLeft [ml], marginStart [ms], marginEnd[me], marginHorizontal [mx], marginVertical [my], padding [p], paddingTop [pt], paddingRight [pr], paddingBottom [pb], paddingLeft [pl], paddingStart [ps], paddingEnd [pe], paddingHorizontal [px], paddingVertical [py], gap [g], rowGap [rG], columnGap [cG]
В квадратных скобках сокращенный вариант пропса
// будет добавлен marginTop=12px и padding=8px <Box marginTop={3} padding={2} />
BorderRadius
Радиус тоже берется из темы и задается следующим образом:
<Box borderColor="g400" borderStyle="solid" borderWidth={1} borderRadius="xs" />
Кастомные стили
Через свойство style можно пробросить и обычные стили. Таким образом можно добавить разные исключения, например, если нужен отступ некратный 4 или цвет не из ДС.
// добавится отступ 5px и кастомный цвет <Box style={{ padding: 5, backgroundColor: '#ccc' }} />
Размеры девайсов
В моей ДС определены два размера девайсов, что позволяет указывать разные стили следующим образом:
// s: от 0px до 360px // m: больше 360px // для s девайсов будет 4px отступ, а для m девайсов 8px <Box marginTop={{ s: 1, m: 2 }} />
При помощи такой конструкции можно пробрасывать любые свойства, не только отступы.
Если написать просто marginTop={1}, то стили применятся для всех устройств (s и m).
Если нам нужно написать какую-то логику в компоненте, опираясь на размеры девайсов, то можно использовать хук useResponsiveProp():
import {useResponsiveProp} from '@shopify/restyle'; export const MyComponent = () => { const value = useResponsiveProp({ s: 'Сейчас S устройство', m: 'Сейчса M устройство' }); return <Text>{value}</Text>; };
Использование токенов из темы
Нам не нужно импортировать нужные токены из темы, можно нужно просто достать их используя хук useTheme().
import {useTheme} from '@shopify/restyle'; export const MyComponent = () => { const {colors: {g300}} = useTheme(); return <OutsideComponent color={g300}>Привет!</OutsideComponent> };
Это может пригодиться, если нам нужно пробросить именно значение цвета в сторонний компонент.
Производительность
Я произвел исследование по производительности 3 вариантов и получил интересные результаты!
Как именно измерял
Для теста был создан следующий компонент:

Компонент содержал 100 строк
В каждой строке был номер, созданный шрифтом из ДС
В каждой строке было 20 блоков, с радиусом из ДС и рандомным цветом из ДС
Проводилось 10 замеров рендера для каждого варианта, затем для результата бралось среднее значение.
Измерялось время рендера следующим образом:
export const Component = () => { const start = new Date().getTime(); useEffect(() => { const renderTime = new Date().getTime() - start; console.log(renderTime); }, []); return ( ... ); };
Вариант UI | Время рендера (милисекунды) |
StyleSheet | 408 |
Restyle | 499 |
Styled-components | 632 |
Оказалось, что Restyle значительно быстрее Styled-components, но естественно немного медленнее вообще нативного решения, потому что содержит дополнительные обертки и парсер атрибутов в стили.
Итог
Что мы получаем от Restyle:
Удобные “строительные блоки”
BoxиTextУдобная работа с токенами ДС через атрибуты
Все токены достаются через хуки из Restyle контекста
Удобочитаемые композиции верстки сразу со стилями
Понятная и однозначная концепция для построения UI
Эта библиотека точно заслуживает внимания. Не вижу причин не брать ее на вооружение прям сейчас! Спасибо за внимание ?
