Skip to content

How native-recipe works

createStyleRecipe and createSlotStyleRecipe create recipes and slot recipes of a style kind on @lynstack/recipe, the engine that selects, caches, and types them. This page covers what React Native styles add; see the engine’s How it works for the rest.

A recipe compiles its config when it is created: it numbers the options of each variant, so a selection becomes one integer. The first call for a selection merges its style and caches it under that integer; every later call with the same variants reads the option of each variant and returns the cached style. Create each recipe once, at the top level of a module, so that its cache lasts. The cache grows with the selections the recipe is called with, up to one entry per combination of declared options. A recipe whose config sets cache: false builds a new style on every call, which changes the style prop on every render, so leave the cache on.

A style merges, from the least to the most specific:

  1. base.
  2. The style of each variant’s selected option, in the order of variants.
  3. The style of each matching compound variant, in the order of compoundVariants.

A later style overrides the properties of an earlier one, as in StyleSheet.flatten, and a property set to undefined is copied too. Defaults apply before compound variants match, so a compound variant can match a default option. A slot recipe merges the style of each slot in the same order.

What makes a style cheap for React Native is its identity. When a component renders again and its style prop is the same object as before, React skips comparing it, and a memoized child that receives it skips rendering. A style built during the render, such as { ...base, ...sizes[size] }, or an array of styles, is a new value on every render, which React compares property by property, and flattens when it is an array.

A recipe returns the same frozen object for the same variants, so the style prop keeps its identity as long as the variants do, as a style of StyleSheet.create declared outside the component does. A slot recipe returns the same frozen object, holding the same frozen style for each slot. Freezing keeps a component from changing a style that every other call with the same variants shares.

A recipe of createThemedRecipes takes a theme and a selection. The first call with a theme object builds the config for that theme and compiles a recipe from it, which it keeps for that object; every later call with the same object uses that recipe and its cache. So:

  • The styles of each theme are cached apart, and switching back to a theme returns its cached styles.
  • A theme object must keep its identity: a new object, even with the same tokens, compiles a new recipe with an empty cache. Create each theme once, or memoize a theme built at runtime (see Themes and design tokens).
  • A theme that is no longer referenced is released with its recipe.

The types accept only the options the config declares. A value from untyped data can still bypass them: an option that its variant does not declare adds no style, and its style is built on every call instead of being cached. Properties of the selection that are not variants are ignored, and calling a recipe without a selection is the same as calling it with an empty one.