Як створювати та використовувати теми в Tamagui. Практичний приклад на основі todo-застосунку
Привіт! Мене звати Артем, я Fullstack-розробник. Для мене програмування — це не просто набір технологій і написання коду, а спосіб мислення.
Я вирішив написати цю статтю, тому що в інтернеті дуже мало інформації про роботу з темами в Tamagui. Ті, хто вже працював із цією бібліотекою, знають, що її документація не завжди є достатньо детальною. Тож я вирішив створити простий приклад — todo-застосунок з трьома темами.

Ця стаття призначена для людей, які вже мають налаштований проєкт з використанням Tamagui, але не знають, як створювати власні теми та ефективно працювати з ними.
Посилання на проєкт на GitHub. Інструкції щодо встановлення та запуску застосунка можна знайти в репозиторії.
Коротко про технології, які я використав:
- Expo
- TypeScript
- Redux Toolkit
- React Native MMKV
- Redux Persist
- Tamagui
- React Native Gesture Handler (для видалення задачі по свайпу)
Основи роботи з темами
Перш за все потрібно розуміти два ключових поняття: палітра (palette) і шаблон (template). Детальніше можна прочитати в офіційній документації Tamagui, але я поясню.
Палітра — це набір кольорів, який використовується в темі. Назви палітр можуть бути довільними. Приклад:
const darkPallete = [ «#111827», «#F9FAFB», «#10B981», «#9CA3AF», «#374151», «#4B5563», «#1F2937», «#10B981», ];
Шаблон — це об’єкт, де кожен колір палітри відповідає певній логічній назві (ключ в об’єкті шаблона), наприклад, background, primary, accent.
Значенням для цих назв є індекси кольорів з палітри. const baseTemplate = { background: 0, border: 5, buttonBg: 7, checkbox: 6, inputBorder: 4, text: 1, textAccent: 2, textMuted: 3, };
Налаштування тем у застосунку
Щоб теми працювали, треба огорнути весь проєкт у компонент TamaguiProvider і передати туди тему за замовчуванням та конфігурацію. Я використовую Redux Toolkit, тому створив окремий слайс для зберігання обраної теми, але можна використовувати будь-який стейт-менеджер — суть залишається тією ж.
theme-slice.ts
import { createSlice } from "@reduxjs/toolkit";
export interface ThemeState {
theme: string;
}
const initialState: ThemeState = {
theme: "light",
};
export const themeSlice = createSlice({
initialState,
name: "theme",
reducers: {
setTheme: (state, action) => {
state.theme = action.payload;
},
},
});
export const { setTheme } = themeSlice.actions;
export default themeSlice.reducer;
tamagui-provider.tsx
import { ReactNode } from "react";
import { useSelector } from "@/store/hooks";
import tamaguiConfig from "@/tamagui.config";
import { TamaguiProvider as Tamagui } from "tamagui";
type TamaguiProviderProps = {
children: ReactNode;
};
export default function TamaguiProvider({ children }: TamaguiProviderProps) {
const theme = useSelector((state) => state.theme.theme);
return (
<Tamagui config={tamaguiConfig} defaultTheme={theme}>
{children}
</Tamagui>
);
}
Як це працює:
- У файлі theme-builder.ts я визначив три палітри: темну, зелену та світлу. Усі вони використовують один шаблон.
theme-builder.ts
import { createThemeBuilder } from
"@tamagui/theme-builder";
const palettes = {
dark: [
"#111827", // background
"#F9FAFB", // text
"#10B981", // textAccent
"#9CA3AF", // textMuted
"#374151", // input border
"#4B5563", // checkbox border
"#1F2937", // checkbox background
"#10B981", // button background
],
green: [
"#D1FAE5", // background
"#065F46", // text
"#10B981", // textAccent
"#6EE7B7", // textMuted
"#A7F3D0", // input border
"#A7F3D0", // checkbox border
"#ECFDF5", // checkbox background
"#10B981", // button background
],
light: [
"#F9FAFB", // background
"#111827", // text
"#3B82F6", // textAccent
"#6B7280", // textMuted
"#E5E7EB", // input border
"#D1D5DB", // checkbox border
"#FFFFFF", // checkbox background
"#3B82F6", // button background
],
};
const templates = {
base: {
background: 0,
border: 5,
buttonBg: 7,
checkbox: 6,
inputBorder: 4,
text: 1,
textAccent: 2,
textMuted: 3,
},
};
- За допомогою функції createThemeBuilder я створив нову тему.
export const themes = createThemeBuilder()
.addPalettes(palettes)
.addTemplates(templates)
.addThemes({
dark: {
palette: "dark",
template: "base",
},
green: {
palette: "green",
template: "base",
},
light: {
palette: "light",
template: "base",
},
})
.build();
- Щоб збілдити тему, додав у package.json скрипт: «generate-themes»: «tamagui generate-themes ./theme-builder.ts ./theme-output.ts». Цей скрипт за допомогою @tamagui/cli генерує теми та зберігає їх у файл theme-output.ts.
Тепер потрібно додати створені нами теми в файл tamagui.config.ts, а саме в функцію createTamagui().
tamagui.config.ts
import { shorthands } from "@tamagui/shorthands";
import { createTamagui, createTokens } from "tamagui";
import {
animations,
tokens as defaultTokens,
fonts,
media,
} from "@tamagui/config/v3";
import * as themes from "./theme-output";
const tokens = createTokens({
...defaultTokens,
});
const config = createTamagui({
animations,
fonts,
media,
shorthands,
themes,
tokens,
});
type Conf = typeof config;
declare module "tamagui" {
interface TamaguiCustomConfig extends Conf {}
}
export default config;
Як використовувати теми
В результаті ми отримуємо велику палітру, шаблон і три готові теми. Тепер їх можна застосовувати в компонентах, використовуючи, наприклад, змінну $background. Однак це не означає, що не можна прописати властивість як color="$background".
Це лише означає, що змінна буде братися на основі активної теми. Але вона залишається звичайним кольором, який можна використовувати в межах стилізації компонентів як завгодно.
<SafeArea>
<AvoidKeyboardView>
<YStack bg="$background" gap="$3" h="100%" padding="$3">
<ThemeSelect />
<H1 color="$textAccent">Tasks</H1>
<H4 color="$text">Title:</H4>
<Input
bbw="$1"
blw={0}
borderColor="$inputBorder"
brw={0}
btw={0}
color="$text"
onChangeText={setTitle}
placeholder="Pay utilities bill"
placeholderTextColor="$textMuted"
value={title}
/>
<Button bg="$buttonBg" color="$text" onPress={() => handleAdd(title)}>
Add todo
</Button>
<TodoList />
</YStack>
</AvoidKeyboardView>
</SafeArea>
Щоб змінити тему, достатньо оновити значення теми у Redux Store, і всі кольори зміняться автоматично.
export default function ThemeSelect({ ...props }: XStackProps) {
const dispatch = useDispatch();
const theme = useSelector((state) => state.theme.theme);
return (
<XStack ai="center" gap="$5" jc="center" p="$3" {...props}>
<ThemeSelectItem
bg="$black4"
isSelected={theme === "dark"}
onPress={() => dispatch(setTheme("dark"))}
/>
<ThemeSelectItem
bg="$green8Light"
isSelected={theme === "green"}
onPress={() => dispatch(setTheme("green"))}
/>
<ThemeSelectItem
bg="$blue8Light"
isSelected={theme === "light"}
onPress={() => dispatch(setTheme("light"))}
/>
</XStack>
);
Висновок
Отже, щоб створити тему в Tamagui, потрібно визначити палітру, шаблон, а потім збілдити тему, яку можна буде додати в tamagui.config ). Створення тем у Tamagui не є складним, але вимагає правильного розуміння базових концепцій і налаштувань. Сподіваюся, ця інформація стане вам корисною!
Якщо вам цікаво, я можу підготувати статтю про більш просунуте використання тем у Tamagui. До речі, я також планую записати відео англійською мовою про роботу з темами :)
Це моя перша стаття, тому судіть строго!
Дякую за увагу!
Немає коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів