Skip to main content

Styling Ignite apps

Ignite's approach to styling individual components is, like many other things in Ignite, straightforward and direct.

If you're looking to set app-wide styles such as fonts/typography or colors, check out the Theming documentation.

We don't use StyleSheet.create() as a general rule, as it doesn't provide any real benefits over bare objects and functions.

We instead use a strategy of bare JS objects and functions that take a theme parameter, colocated with our components (usually below the component in the file), prefixed with $, and typed with TypeScript:

import { View, type ViewStyle } from "react-native"
import { useAppTheme } from "@/utils/useAppTheme"

const MyComponent = () => {
const { themed } = useAppTheme()
return (
<View style={themed($container)}>
<View style={$plainObjectStyle} />
</View>
)
}

const $container: ThemedStyle<ViewStyle> = (theme) => ({
flex: 1,
backgroundColor: theme.colors.background,
paddingHorizontal: theme.spacing.small,
})

const $plainObjectStyle: ViewStyle = {
marginBottom: 20,
}

We use components with presets to share styles across our whole app.

With this strategy, you can tell if a variable is a style when it has the $ prefix. You can also spread in other styles to compose styles:

const $bold: TextStyle = {
fontWeight: "bold",
}
const $larger: TextStyle = {
fontSize: 22,
}
const $title: TextStyle = {
...$bold,
...$larger,
}

Sharing Styles via Presets

Most of the components we include with Ignite include a preset property:

<View preset="heading" text="My Header" />

Presets are defined in the component file itself, usually something like this:

type Presets = "default" | "bold" | "heading" | "subheading"
const $presets: Record<Presets, ThemedStyleArray<TextStyle>> = {
default: [$baseStyle],
bold: [$baseStyle, $fontWeightStyles.bold],
heading: [$baseStyle, $sizeStyles.xxl, $fontWeightStyles.bold],
subheading: [$baseStyle, $sizeStyles.lg, $fontWeightStyles.medium],
}

These presets are usually composed of other styles, using arrays (which React Native will properly merge).

So, let's say we want a button that is a destructive action. We might add a "destructive" preset to the Button component.

The preset might look like this:

const $warning: ThemedStyle<ViewStyle> = (theme) => ({
backgroundColor: theme.colors.alert,
color: "white",
padding: theme.spacing.lg,
})

const $viewPresets: Record<Presets, ThemedStyle<ViewStyle>> = {
destructive: [$baseViewStyle, $warning],
}

You can then use it with your Button like this:

<Button
text="Delete"
// set the preset here
preset="destructive"
onPress={() => thisItem.destroy()}
/>