Installation
To get started with gluestack-ui v2, check out this quick installation guide. It provides simple steps to help you install and use the library in your projects.
CLI
Manual
Step 1: Setup your project
If you are starting with a new project, setup your project with Next.js or Expo. If you already have a project, you can skip this step.
Prerequisites
Ensure you have the following prerequisites installed on your project to use gluestack-ui CLI:
Package Name | Supported Versions | Upcoming Support |
---|---|---|
next | 13 <= versions <= 14 | version 15 (Next.js 15 Support is Manual Only for Now. Please check the Additional Step for Next.js 15) |
react-native | versions >= 72.5 | - |
expo | versions >= 50 | - |
node | versions > 16 | - |
Step 2: Initialize
Go to your project, and use the init command at the root of your project to add GluestackUIProvider and the gluestack-ui-provider/config.ts file to your project..
npx gluestack-ui init
Important Note
Installation using gluestack-ui CLI in Expo projects supports for Expo SDK 50 and above only. For Expo SDK < 49, please refer to the manual installation.
Your project is now ready to use gluestack-ui components. To add gluestack-ui components to your project using the CLI, refer to the above command or use the CLI guide.
npx gluestack-ui add box
If you encounter issues during the CLI installation, refer to the manual installation guide available.
Step 1: Setup your project.
Setup nativewind in your project following NativeWind documentation.
Step 2: Install dependencies
Install the dependencies of gluestack-ui in your project. This can be done using the following command:
yarn add @gluestack-ui/nativewind-utils @gluestack-ui/overlay @gluestack-ui/toast react-native-svg
OR
npm i @gluestack-ui/nativewind-utils @gluestack-ui/overlay @gluestack-ui/toast react-native-svg
Step 2.1: Update Tailwind configuration
Update the tailwind.config.js file with the following code and also the content property with paths to the files where components are used.
import gluestackPlugin from '@gluestack-ui/nativewind-utils/tailwind-plugin';/** @type {import('tailwindcss').Config} */module.exports = {darkMode: process.env.DARK_MODE ? process.env.DARK_MODE : 'media',content: ['./src/**/*.{html,js,jsx,ts,tsx}','./src/core-components/**/**/*.{html,js,jsx,ts,tsx}','./src/components/**/*.{html,js,jsx,ts,tsx,mdx}','./src/hooks/**/*.{html,js,jsx,ts,tsx,mdx}',],presets: [require('nativewind/preset')],theme: {extend: {colors: {primary: {0: 'rgb(var(--color-primary-0)/<alpha-value>)',50: 'rgb(var(--color-primary-50)/<alpha-value>)',100: 'rgb(var(--color-primary-100)/<alpha-value>)',200: 'rgb(var(--color-primary-200)/<alpha-value>)',300: 'rgb(var(--color-primary-300)/<alpha-value>)',400: 'rgb(var(--color-primary-400)/<alpha-value>)',500: 'rgb(var(--color-primary-500)/<alpha-value>)',600: 'rgb(var(--color-primary-600)/<alpha-value>)',700: 'rgb(var(--color-primary-700)/<alpha-value>)',800: 'rgb(var(--color-primary-800)/<alpha-value>)',900: 'rgb(var(--color-primary-900)/<alpha-value>)',950: 'rgb(var(--color-primary-950)/<alpha-value>)',},secondary: {0: 'rgb(var(--color-secondary-0)/<alpha-value>)',50: 'rgb(var(--color-secondary-50)/<alpha-value>)',100: 'rgb(var(--color-secondary-100)/<alpha-value>)',200: 'rgb(var(--color-secondary-200)/<alpha-value>)',300: 'rgb(var(--color-secondary-300)/<alpha-value>)',400: 'rgb(var(--color-secondary-400)/<alpha-value>)',500: 'rgb(var(--color-secondary-500)/<alpha-value>)',600: 'rgb(var(--color-secondary-600)/<alpha-value>)',700: 'rgb(var(--color-secondary-700)/<alpha-value>)',800: 'rgb(var(--color-secondary-800)/<alpha-value>)',900: 'rgb(var(--color-secondary-900)/<alpha-value>)',950: 'rgb(var(--color-secondary-950)/<alpha-value>)',},tertiary: {50: 'rgb(var(--color-tertiary-50)/<alpha-value>)',100: 'rgb(var(--color-tertiary-100)/<alpha-value>)',200: 'rgb(var(--color-tertiary-200)/<alpha-value>)',300: 'rgb(var(--color-tertiary-300)/<alpha-value>)',400: 'rgb(var(--color-tertiary-400)/<alpha-value>)',500: 'rgb(var(--color-tertiary-500)/<alpha-value>)',600: 'rgb(var(--color-tertiary-600)/<alpha-value>)',700: 'rgb(var(--color-tertiary-700)/<alpha-value>)',800: 'rgb(var(--color-tertiary-800)/<alpha-value>)',900: 'rgb(var(--color-tertiary-900)/<alpha-value>)',950: 'rgb(var(--color-tertiary-950)/<alpha-value>)',},error: {0: 'rgb(var(--color-error-0)/<alpha-value>)',50: 'rgb(var(--color-error-50)/<alpha-value>)',100: 'rgb(var(--color-error-100)/<alpha-value>)',200: 'rgb(var(--color-error-200)/<alpha-value>)',300: 'rgb(var(--color-error-300)/<alpha-value>)',400: 'rgb(var(--color-error-400)/<alpha-value>)',500: 'rgb(var(--color-error-500)/<alpha-value>)',600: 'rgb(var(--color-error-600)/<alpha-value>)',700: 'rgb(var(--color-error-700)/<alpha-value>)',800: 'rgb(var(--color-error-800)/<alpha-value>)',900: 'rgb(var(--color-error-900)/<alpha-value>)',950: 'rgb(var(--color-error-950)/<alpha-value>)',},success: {0: 'rgb(var(--color-success-0)/<alpha-value>)',50: 'rgb(var(--color-success-50)/<alpha-value>)',100: 'rgb(var(--color-success-100)/<alpha-value>)',200: 'rgb(var(--color-success-200)/<alpha-value>)',300: 'rgb(var(--color-success-300)/<alpha-value>)',400: 'rgb(var(--color-success-400)/<alpha-value>)',500: 'rgb(var(--color-success-500)/<alpha-value>)',600: 'rgb(var(--color-success-600)/<alpha-value>)',700: 'rgb(var(--color-success-700)/<alpha-value>)',800: 'rgb(var(--color-success-800)/<alpha-value>)',900: 'rgb(var(--color-success-900)/<alpha-value>)',950: 'rgb(var(--color-success-950)/<alpha-value>)',},warning: {0: 'rgb(var(--color-warning-0)/<alpha-value>)',50: 'rgb(var(--color-warning-50)/<alpha-value>)',100: 'rgb(var(--color-warning-100)/<alpha-value>)',200: 'rgb(var(--color-warning-200)/<alpha-value>)',300: 'rgb(var(--color-warning-300)/<alpha-value>)',400: 'rgb(var(--color-warning-400)/<alpha-value>)',500: 'rgb(var(--color-warning-500)/<alpha-value>)',600: 'rgb(var(--color-warning-600)/<alpha-value>)',700: 'rgb(var(--color-warning-700)/<alpha-value>)',800: 'rgb(var(--color-warning-800)/<alpha-value>)',900: 'rgb(var(--color-warning-900)/<alpha-value>)',950: 'rgb(var(--color-warning-950)/<alpha-value>)',},info: {0: 'rgb(var(--color-info-0)/<alpha-value>)',50: 'rgb(var(--color-info-50)/<alpha-value>)',100: 'rgb(var(--color-info-100)/<alpha-value>)',200: 'rgb(var(--color-info-200)/<alpha-value>)',300: 'rgb(var(--color-info-300)/<alpha-value>)',400: 'rgb(var(--color-info-400)/<alpha-value>)',500: 'rgb(var(--color-info-500)/<alpha-value>)',600: 'rgb(var(--color-info-600)/<alpha-value>)',700: 'rgb(var(--color-info-700)/<alpha-value>)',800: 'rgb(var(--color-info-800)/<alpha-value>)',900: 'rgb(var(--color-info-900)/<alpha-value>)',950: 'rgb(var(--color-info-950)/<alpha-value>)',},typography: {0: 'rgb(var(--color-typography-0)/<alpha-value>)',50: 'rgb(var(--color-typography-50)/<alpha-value>)',100: 'rgb(var(--color-typography-100)/<alpha-value>)',200: 'rgb(var(--color-typography-200)/<alpha-value>)',300: 'rgb(var(--color-typography-300)/<alpha-value>)',400: 'rgb(var(--color-typography-400)/<alpha-value>)',500: 'rgb(var(--color-typography-500)/<alpha-value>)',600: 'rgb(var(--color-typography-600)/<alpha-value>)',700: 'rgb(var(--color-typography-700)/<alpha-value>)',800: 'rgb(var(--color-typography-800)/<alpha-value>)',900: 'rgb(var(--color-typography-900)/<alpha-value>)',950: 'rgb(var(--color-typography-950)/<alpha-value>)',white: '#FFFFFF',gray: '#D4D4D4',black: '#181718',},outline: {0: 'rgb(var(--color-outline-0)/<alpha-value>)',50: 'rgb(var(--color-outline-50)/<alpha-value>)',100: 'rgb(var(--color-outline-100)/<alpha-value>)',200: 'rgb(var(--color-outline-200)/<alpha-value>)',300: 'rgb(var(--color-outline-300)/<alpha-value>)',400: 'rgb(var(--color-outline-400)/<alpha-value>)',500: 'rgb(var(--color-outline-500)/<alpha-value>)',600: 'rgb(var(--color-outline-600)/<alpha-value>)',700: 'rgb(var(--color-outline-700)/<alpha-value>)',800: 'rgb(var(--color-outline-800)/<alpha-value>)',900: 'rgb(var(--color-outline-900)/<alpha-value>)',950: 'rgb(var(--color-outline-950)/<alpha-value>)',},background: {0: 'rgb(var(--color-background-0)/<alpha-value>)',50: 'rgb(var(--color-background-50)/<alpha-value>)',100: 'rgb(var(--color-background-100)/<alpha-value>)',200: 'rgb(var(--color-background-200)/<alpha-value>)',300: 'rgb(var(--color-background-300)/<alpha-value>)',400: 'rgb(var(--color-background-400)/<alpha-value>)',500: 'rgb(var(--color-background-500)/<alpha-value>)',600: 'rgb(var(--color-background-600)/<alpha-value>)',700: 'rgb(var(--color-background-700)/<alpha-value>)',800: 'rgb(var(--color-background-800)/<alpha-value>)',900: 'rgb(var(--color-background-900)/<alpha-value>)',950: 'rgb(var(--color-background-950)/<alpha-value>)',error: 'rgb(var(--color-background-error)/<alpha-value>)',warning: 'rgb(var(--color-background-warning)/<alpha-value>)',muted: 'rgb(var(--color-background-muted)/<alpha-value>)',success: 'rgb(var(--color-background-success)/<alpha-value>)',info: 'rgb(var(--color-background-info)/<alpha-value>)',light: '#FBFBFB',dark: '#181719',},indicator: {primary: 'rgb(var(--color-indicator-primary)/<alpha-value>)',info: 'rgb(var(--color-indicator-info)/<alpha-value>)',error: 'rgb(var(--color-indicator-error)/<alpha-value>)',},},fontFamily: {heading: undefined,body: undefined,mono: undefined,roboto: ['Roboto', 'sans-serif'],},fontWeight: {extrablack: '950',},fontSize: {'2xs': '10px',},boxShadow: {'hard-1': '-2px 2px 8px 0px rgba(38, 38, 38, 0.20)','hard-2': '0px 3px 10px 0px rgba(38, 38, 38, 0.20)','hard-3': '2px 2px 8px 0px rgba(38, 38, 38, 0.20)','hard-4': '0px -3px 10px 0px rgba(38, 38, 38, 0.20)','hard-5': '0px 2px 10px 0px rgba(38, 38, 38, 0.10)','soft-1': '0px 0px 10px rgba(38, 38, 38, 0.1)','soft-2': '0px 0px 20px rgba(38, 38, 38, 0.2)','soft-3': '0px 0px 30px rgba(38, 38, 38, 0.1)','soft-4': '0px 0px 40px rgba(38, 38, 38, 0.1)',},},},plugins: [gluestackPlugin],};
Step 2.2: Configure components path
Create a components/ui folder inside src folder and add path in tsconfig.json
// ... (other configs)compilerOptions: {// ..."paths": {// ..."@/*": ["./*"] // Add relative path},}
Step 2.3: Configure GluestackUIProvider
To add config, create a gluestack-ui-provider/config.ts file in your components/ui folder and paste the following code.
"use client"import { vars } from "nativewind"export const config = {light: vars({"--color-primary-0": "179 179 179","--color-primary-50": "153 153 153","--color-primary-100": "128 128 128","--color-primary-200": "115 115 115","--color-primary-300": "102 102 102","--color-primary-400": "82 82 82","--color-primary-500": "51 51 51","--color-primary-600": "41 41 41","--color-primary-700": "31 31 31","--color-primary-800": "13 13 13","--color-primary-900": "10 10 10","--color-primary-950": "8 8 8",/* Secondary */"--color-secondary-0": "253 253 253","--color-secondary-50": "251 251 251","--color-secondary-100": "246 246 246","--color-secondary-200": "242 242 242","--color-secondary-300": "237 237 237","--color-secondary-400": "230 230 231","--color-secondary-500": "217 217 219","--color-secondary-600": "198 199 199","--color-secondary-700": "189 189 189","--color-secondary-800": "177 177 177","--color-secondary-900": "165 164 164","--color-secondary-950": "157 157 157",/* Tertiary */"--color-tertiary-0": "255 250 245","--color-tertiary-50": "255 242 229","--color-tertiary-100": "255 233 213","--color-tertiary-200": "254 209 170","--color-tertiary-300": "253 180 116","--color-tertiary-400": "251 157 75","--color-tertiary-500": "231 129 40","--color-tertiary-600": "215 117 31","--color-tertiary-700": "180 98 26","--color-tertiary-800": "130 73 23","--color-tertiary-900": "108 61 19","--color-tertiary-950": "84 49 18",/* Error */"--color-error-0": "254 233 233","--color-error-50": "254 226 226","--color-error-100": "254 202 202","--color-error-200": "252 165 165","--color-error-300": "248 113 113","--color-error-400": "239 68 68","--color-error-500": "230 53 53","--color-error-600": "220 38 38","--color-error-700": "185 28 28","--color-error-800": "153 27 27","--color-error-900": "127 29 29","--color-error-950": "83 19 19",/* Success */"--color-success-0": "228 255 244","--color-success-50": "202 255 232","--color-success-100": "162 241 192","--color-success-200": "132 211 162","--color-success-300": "102 181 132","--color-success-400": "72 151 102","--color-success-500": "52 131 82","--color-success-600": "42 121 72","--color-success-700": "32 111 62","--color-success-800": "22 101 52","--color-success-900": "20 83 45","--color-success-950": "27 50 36",/* Warning */"--color-warning-0": "255 249 245","--color-warning-50": "255 244 236","--color-warning-100": "255 231 213","--color-warning-200": "254 205 170","--color-warning-300": "253 173 116","--color-warning-400": "251 149 75","--color-warning-500": "231 120 40","--color-warning-600": "215 108 31","--color-warning-700": "180 90 26","--color-warning-800": "130 68 23","--color-warning-900": "108 56 19","--color-warning-950": "84 45 18",/* Info */"--color-info-0": "236 248 254","--color-info-50": "199 235 252","--color-info-100": "162 221 250","--color-info-200": "124 207 248","--color-info-300": "87 194 246","--color-info-400": "50 180 244","--color-info-500": "13 166 242","--color-info-600": "11 141 205","--color-info-700": "9 115 168","--color-info-800": "7 90 131","--color-info-900": "5 64 93","--color-info-950": "3 38 56",/* Typography */"--color-typography-0": "254 254 255","--color-typography-50": "245 245 245","--color-typography-100": "229 229 229","--color-typography-200": "219 219 220","--color-typography-300": "212 212 212","--color-typography-400": "163 163 163","--color-typography-500": "140 140 140","--color-typography-600": "115 115 115","--color-typography-700": "82 82 82","--color-typography-800": "64 64 64","--color-typography-900": "38 38 39","--color-typography-950": "23 23 23",/* Outline */"--color-outline-0": "253 254 254","--color-outline-50": "243 243 243","--color-outline-100": "230 230 230","--color-outline-200": "221 220 219","--color-outline-300": "211 211 211","--color-outline-400": "165 163 163","--color-outline-500": "140 141 141","--color-outline-600": "115 116 116","--color-outline-700": "83 82 82","--color-outline-800": "65 65 65","--color-outline-900": "39 38 36","--color-outline-950": "26 23 23",/* Background */"--color-background-0": "255 255 255","--color-background-50": "246 246 246","--color-background-100": "242 241 241","--color-background-200": "220 219 219","--color-background-300": "213 212 212","--color-background-400": "162 163 163","--color-background-500": "142 142 142","--color-background-600": "116 116 116","--color-background-700": "83 82 82","--color-background-800": "65 64 64","--color-background-900": "39 38 37","--color-background-950": "18 18 18",/* Background Special */"--color-background-error": "254 241 241","--color-background-warning": "255 243 234","--color-background-success": "237 252 242","--color-background-muted": "247 248 247","--color-background-info": "235 248 254",/* Focus Ring Indicator */"--color-indicator-primary": "55 55 55","--color-indicator-info": "83 153 236","--color-indicator-error": "185 28 28",}),dark: vars({"--color-primary-0": "166 166 166","--color-primary-50": "175 175 175","--color-primary-100": "186 186 186","--color-primary-200": "197 197 197","--color-primary-300": "212 212 212","--color-primary-400": "221 221 221","--color-primary-500": "230 230 230","--color-primary-600": "240 240 240","--color-primary-700": "250 250 250","--color-primary-800": "253 253 253","--color-primary-900": "254 249 249","--color-primary-950": "253 252 252",/* Secondary */"--color-secondary-0": "20 20 20","--color-secondary-50": "23 23 23","--color-secondary-100": "31 31 31","--color-secondary-200": "39 39 39","--color-secondary-300": "44 44 44","--color-secondary-400": "56 57 57","--color-secondary-500": "63 64 64","--color-secondary-600": "86 86 86","--color-secondary-700": "110 110 110","--color-secondary-800": "135 135 135","--color-secondary-900": "150 150 150","--color-secondary-950": "164 164 164",/* Tertiary */"--color-tertiary-0": "84 49 18","--color-tertiary-50": "108 61 19","--color-tertiary-100": "130 73 23","--color-tertiary-200": "180 98 26","--color-tertiary-300": "215 117 31","--color-tertiary-400": "231 129 40","--color-tertiary-500": "251 157 75","--color-tertiary-600": "253 180 116","--color-tertiary-700": "254 209 170","--color-tertiary-800": "255 233 213","--color-tertiary-900": "255 242 229","--color-tertiary-950": "255 250 245",/* Error */"--color-error-0": "83 19 19","--color-error-50": "127 29 29","--color-error-100": "153 27 27","--color-error-200": "185 28 28","--color-error-300": "220 38 38","--color-error-400": "230 53 53","--color-error-500": "239 68 68","--color-error-600": "249 97 96","--color-error-700": "229 91 90","--color-error-800": "254 202 202","--color-error-900": "254 226 226","--color-error-950": "254 233 233",/* Success */"--color-success-0": "27 50 36","--color-success-50": "20 83 45","--color-success-100": "22 101 52","--color-success-200": "32 111 62","--color-success-300": "42 121 72","--color-success-400": "52 131 82","--color-success-500": "72 151 102","--color-success-600": "102 181 132","--color-success-700": "132 211 162","--color-success-800": "162 241 192","--color-success-900": "202 255 232","--color-success-950": "228 255 244",/* Warning */"--color-warning-0": "84 45 18","--color-warning-50": "108 56 19","--color-warning-100": "130 68 23","--color-warning-200": "180 90 26","--color-warning-300": "215 108 31","--color-warning-400": "231 120 40","--color-warning-500": "251 149 75","--color-warning-600": "253 173 116","--color-warning-700": "254 205 170","--color-warning-800": "255 231 213","--color-warning-900": "255 244 237","--color-warning-950": "255 249 245",/* Info */"--color-info-0": "3 38 56","--color-info-50": "5 64 93","--color-info-100": "7 90 131","--color-info-200": "9 115 168","--color-info-300": "11 141 205","--color-info-400": "13 166 242","--color-info-500": "50 180 244","--color-info-600": "87 194 246","--color-info-700": "124 207 248","--color-info-800": "162 221 250","--color-info-900": "199 235 252","--color-info-950": "236 248 254",/* Typography */"--color-typography-0": "23 23 23","--color-typography-50": "38 38 39","--color-typography-100": "64 64 64","--color-typography-200": "82 82 82","--color-typography-300": "115 115 115","--color-typography-400": "140 140 140","--color-typography-500": "163 163 163","--color-typography-600": "212 212 212","--color-typography-700": "219 219 220","--color-typography-800": "229 229 229","--color-typography-900": "245 245 245","--color-typography-950": "254 254 255",/* Outline */"--color-outline-0": "26 23 23","--color-outline-50": "39 38 36","--color-outline-100": "65 65 65","--color-outline-200": "83 82 82","--color-outline-300": "115 116 116","--color-outline-400": "140 141 141","--color-outline-500": "165 163 163","--color-outline-600": "211 211 211","--color-outline-700": "221 220 219","--color-outline-800": "230 230 230","--color-outline-900": "243 243 243","--color-outline-950": "253 254 254",/* Background */"--color-background-0": "18 18 18","--color-background-50": "39 38 37","--color-background-100": "65 64 64","--color-background-200": "83 82 82","--color-background-300": "116 116 116","--color-background-400": "142 142 142","--color-background-500": "162 163 163","--color-background-600": "213 212 212","--color-background-700": "229 228 228","--color-background-800": "242 241 241","--color-background-900": "246 246 246","--color-background-950": "255 255 255",/* Background Special */"--color-background-error": "66 43 43","--color-background-warning": "65 47 35","--color-background-success": "28 43 33","--color-background-muted": "51 51 51","--color-background-info": "26 40 46",/* Focus Ring Indicator */"--color-indicator-primary": "247 247 247","--color-indicator-info": "161 199 245","--color-indicator-error": "232 70 69",}),}
For Native
To add GluestackUIProvider, create a gluestack-ui-provider/index.tsx file inside components/ui folder and paste the following code.
import React from "react"import { config } from "./config"import { ColorSchemeName, useColorScheme, View, ViewProps } from "react-native"import { OverlayProvider } from "@gluestack-ui/overlay"import { ToastProvider } from "@gluestack-ui/toast"import { colorScheme as colorSchemeNW } from "nativewind"type ModeType = "light" | "dark" | "system"const getColorSchemeName = (colorScheme: ColorSchemeName,mode: ModeType): "light" | "dark" => {if (mode === "system") {return colorScheme ?? "light"}return mode}export function GluestackUIProvider({mode = "light",...props}: {mode?: "light" | "dark" | "system"children?: React.ReactNodestyle?: ViewProps["style"]}) {const colorScheme = useColorScheme()const colorSchemeName = getColorSchemeName(colorScheme, mode)colorSchemeNW.set(mode)return (<Viewstyle={[config[colorSchemeName],// eslint-disable-next-line react-native/no-inline-styles{ flex: 1, height: "100%", width: "100%" },props.style,]}><OverlayProvider><ToastProvider>{props.children}</ToastProvider></OverlayProvider></View>)}
For Web
To add GluestackUIProvider, create a gluestack-ui-provider/index.web.tsx file inside components/ui folder and paste the following code.
"use client"import React, { useEffect, useLayoutEffect } from "react"import { config } from "./config"import { OverlayProvider } from "@gluestack-ui/overlay"import { ToastProvider } from "@gluestack-ui/toast"import { setFlushStyles } from "@gluestack-ui/nativewind-utils/flush"import { script } from "./script"const variableStyleTagId = "nativewind-style"const createStyle = (styleTagId: string) => {const style = document.createElement("style")style.id = styleTagIdstyle.appendChild(document.createTextNode(""))return style}export const useSafeLayoutEffect =typeof window !== "undefined" ? useLayoutEffect : useEffectexport function GluestackUIProvider({mode = "light",...props}: {mode?: "light" | "dark" | "system"children?: React.ReactNode}) {let cssVariablesWithMode = ``Object.keys(config).forEach((configKey) => {cssVariablesWithMode +=configKey === "dark" ? `\n .dark {\n ` : `\n:root {\n`const cssVariables = Object.keys(config[configKey as keyof typeof config]).reduce((acc: string, curr: string) => {acc += `${curr}:${config[configKey as keyof typeof config][curr]}; `return acc}, "")cssVariablesWithMode += `${cssVariables} \n}`})setFlushStyles(cssVariablesWithMode)const handleMediaQuery = React.useCallback((e: MediaQueryListEvent) => {script(e.matches ? "dark" : "light")}, [])useSafeLayoutEffect(() => {if