Shopify / restyle

A type-enforced system for building UI components in React Native with TypeScript.

Home Page:https://shopify.github.io/restyle/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How can I inject project theme into library

chj-damon opened this issue · comments

I use restyle to build my react-native component library, there's a theme file exists in the library.
I now start to build a project and I want to use restyle too.
then the question is, How can I inject the project theme into the library so the components can follow the same theme with the app?
Or, if I misunderstood the scenario, what is the best practice about it?

another question is, I can define some variants in my project theme, But I want one component in my library to use it. It won't work right now because the component in my library can only recognize the variants I defined in the library theme.
What should I do to achieve this, or maybe it's impossible?

Hey @chj-damon
With one of my client app, I have restyle and KittenUI, both working like a charm.
It a complex topic because you have sooo many variables but here is my example to share colors defined in KittenUI and used in restyle.

1. A Shared theme Provider
with both libraries, it helps if you want to add a storybook decorator

// src/components/Providers/ThemeProvider.tsx

import React from "react";
import { ApplicationProvider as KittenProvider } from "@ui-kitten/components";
import * as eva from "@eva-design/eva";
import { ThemeProvider as RestyleProvider } from "@shopify/restyle";

import { ProvidersProps } from "./Providers";

import { default as kittenTheme } from "~/constants/app-theme.json";
import restyleTheme from "~/constants/theme";

const ThemeProvider = ({ children }: ProvidersProps) => {
  return (
    <KittenProvider {...eva} theme={{ ...eva, ...kittenTheme }}>
      <RestyleProvider theme={restyleTheme}>{children}</RestyleProvider>
    </KittenProvider>
  );
};

export default ThemeProvider;

2. import colors from KittenUI

// src/constants/theme.ts

import { createTheme } from "@shopify/restyle";

import { default as palette } from "./app-theme.json"; <----- KittenUI theme

const restyleTheme = createTheme({
  colors: {
    ...palette,   <------ inject the palette from KittenUI
  },
  spacing: {
    xs: 4,
    s: 8,
    m: 16,
    l: 24,
    xl: 32,
    "2xl": 48,
    "3xl": 96,
    "4xl": 192,
    "5xl": 256,
  },
  breakpoints: {
    phone: 0,
    tablet: 768,
  },
  borderRadii: {
    xs: 4,
    s: 16,
    m: 24,
    l: 64,
  },
});

export type Theme = typeof restyleTheme;
export default restyleTheme;

I have drawbacks, it's not perfect.
Some issues with the Typography component.
They are not shared yet and it's a lot of effort to make it happen.
It's possible but I have other features to ship before. 😅

Does my answer helped ?

@flexbox thanks for you reply, But I think I have different scenario:

I use restyle in both my project and my UI library, so I defined the theme by importing it from the library and extend it, like this:

import { theme as libraryTheme } from 'ui-library';

export const projectTheme = {
    ...libraryTheme,
    colors: {
        ...libraryTheme.colors,
        // custom colors
    },
    textVariants: {
        ...libraryTheme.textVariants,
        // custom text variants
    }
}

in this way, I can override the pre-defined variables in the library, it works fine.

But, If I want the library to accept some extended variants or colors, it won't work, because the components in the library is not aware of these variables which are not defined in the library theme.

for example, If I want to define another textVariants and use Text component which is exported by the library, the Text component is coded like this:

import { createText } from '@shopify/restyle';
import { Theme } from '../theme';

const Text = createText<Theme>();

export default Text;

you see, Text component is using the theme in the library, which means <Text variant="" /> can only use the textVariants in the library theme too. In my project I have to define another Text component again.

I wonder if I'm using restyle the wrong way....

any updates nowadays?

One possible solution can be passing a generic theme type to your component:

import React from 'react';
import {TextProps as ReactNativeTextProps} from 'react-native';
import {BaseTheme, createText, TextProps} from '@shopify/restyle';

const Text = <Theme extends BaseTheme>(
  props: TextProps<Theme> & ReactNativeTextProps,
) => {
  const Comp = createText<Theme>();
  return <Comp {...props} />;
};

export default Text;

To consume it u can do something like:

<Text<Theme> variant={'button2'} >I am a text</Text>

To avoid passing Theme type each time, u can encapsulate the Text component somewhere and consume it from there instead of directly from the library.

Also, if your theme is in a specific format, u can force the passed generic Theme to extend your theme instead of BaseTheme from restyle.

Keep in mind that this can lead to a performance issue so u can consider memoizing createText:

const Comp = useMemo(() => createText<Theme>(), []);

Or move it outside the component with any as generic.