rofrischmann/fela

View on GitHub
website/docs/11.7.0/guides/usage-with-react.mdx

Summary

Maintainability
Test Coverage
# Usage with React

Fela was always designed with React in mind, but is **not** bound to React by default.<br />
If you want to use it with React, you should also install the official [React bindings for Fela](https://github.com/robinweser/fela/tree/master/packages/react-fela).

```bash
yarn add react-fela
```

## Passing the Renderer

In order to style components, we first need to wrap our application with the [RendererProvider](api/react-fela/RendererProvider) component.<br />
It leverages React's [context](https://reactjs.org/docs/context.html) APIs to pass our [Renderer](basics/renderer) to all child components.

```jsx name=App.js
import { createRenderer } from 'fela'
import { RendererProvider } from 'react-fela'

import MyApp from './MyApp'

const renderer = createRenderer()

export default () => (
  <RendererProvider renderer={renderer}>
    <MyApp />
  </RendererProvider>
)
```

## Styling Components

With [hooks](https://reactjs.org/docs/hooks-intro.html) being added to React, styling your components with Fela has never been easier. <br />
react-fela ships the `useFela` hook which returns an object containing a `css` function. <br/>
Calling that function will render all passed styles and return the generated class names.

```jsx name=Button.js
import React from 'react'
import { useFela } from 'react-fela'

function Button({ children, fontSize = 16 }) {
  const { css } = useFela()

  return (
    <button
      className={css({
        padding: 10,
        fontSize,
        color: 'darkblue',
        ':hover': {
          color: 'blue',
        },
      })}>
      {children}
    </button>
  )
}
```

### Using Rules

Instead of passing style objects directly, you might want to extract your styles into [rules](basics/rules).<br />
The `css` function also accepts rules. We can pass our props to `useFela` in order to access them inside our rules.

```jsx name=Button.js
import React from 'react'
import { useFela } from 'react-fela'

const rule = ({ fontSize }) => ({
  padding: 10,
  fontSize,
  color: 'darkblue',
  ':hover': {
    color: 'blue',
  },
})

function Button({ children, fontSize = 16 }) {
  const { css } = useFela({ fontSize })

  return <button className={css(rule)}>{children}</button>
}
```

### Composition

The `css` function does not only take a single argument, but can take an array of style objects and rules.<br />
They are merged using [`combineRules`](api/fela/combineRules) thus the order of arguments matters.

```jsx name=Button.js
import React from 'react'
import { useFela } from 'react-fela'

const baseStyle = ({ fontSize }) => ({
  padding: 10,
  fontSize: 16,
})

const hoverStyle = {
  color: 'darkblue',
  ':hover': {
    color: 'blue',
  },
}

function Button({ children, fontSize = 16, extend }) {
  const { css } = useFela({ fontSize })

  return (
    <button className={css([baseStyle, hoverStyle, extend])}>{children}</button>
  )
}

// extending the Button styles for a more specific big button
const BigButton = ({ children }) => (
  <Button fontSize={20} extend={{ padding: 16 }}>
    {children}
  </Button>
)
```

### Fonts & Keyframes

The `useFela` hook also returns our [Renderer](basics/renderer) instance, so we can use that to render fonts, keyframes and static styles directly within our components.

> **Tip**: We can also use [fela-plugin-embedded](https://github.com/robinweser/fela/tree/master/packages/fela-plugin-embedded) to render fonts and keyframes from within our style object.

```jsx name=Button.js
import React from 'react'
import { useFela } from 'react-fela'

function Button({ children, fontSize = 16 }) {
  const { css, renderer } = useFela()

  // since fonts and keyframes are cached, they are only rendered once
  const fontFamily = renderer.renderFont('Arial', [
    '/fonts/Arial.ttf',
    '/fonts/Arial.woff',
    '/fonts/Arial.woff2',
  ])

  return (
    <button
      className={css({
        padding: 10,
        fontSize,
        fontFamily,
        color: 'darkblue',
        ':hover': {
          color: 'blue',
        },
      })}>
      {children}
    </button>
  )
}
```

## Theming

### Passing the Theme

In order to access our theme, we need to wrap our application with the [ThemeProvider](api/react-fela/ThemeProvider) component.<br />
Similar to the `RendererProvider` it uses React's context API to pass the theme to every child component.

> **Tip**: We can also wrap specific sub-trees with different themes e.g. if only a specific part of our application should be rendered in dark mode. Multiple nested ThemeProvider instances will automatically deep-merge the theme objects unless a `overwrite` prop is passed with `false`.

```jsx name=App.js
import { createRenderer } from 'fela'
import { RendererProvider, ThemeProvider } from 'react-fela'

import MyApp from './MyApp'
import theme from './theme'

const renderer = createRenderer()

export default () => (
  <RendererProvider renderer={renderer}>
    <ThemeProvider theme={theme}>
      <MyApp />
    </ThemeProvider>
  </RendererProvider>
)
```

### Using the Theme

Next to the `css` function, `useFela` also provides the current `theme` object.

> **Note**: Rules get the theme passed automatically via `props.theme`.

```jsx name=Button.js
import React from 'react'
import { useFela } from 'react-fela'

function Button({ children, fontSize = 16 }) {
  const { css, theme } = useFela()

  return (
    <button
      className={css({
        padding: 10,
        fontSize,
        color: theme.colors.blueDark,
        ':hover': {
          color: theme.colors.blue,
        },
      })}>
      {children}
    </button>
  )
}
```

## Pre-hooks APIs

Prior to hooks, Fela shipped a couple of Higher-order and Render-props components to style React components.<br />
While we recommend using the `useFela` hook whenever possible, they are still valid APIs and can be used interchangable.

- [FelaComponent](api/react-fela/FelaComponent)
- [FelaTheme](api/react-fela/FelaTheme)
- [FelaRenderer](api/react-fela/FelaRenderer)
- [createComponent](api/react-fela/createComponent)
- [createComponentWithProxy](api/react-fela/createComponentWithProxy)
- [connect](api/react-fela/connect)
- [withTheme](api/react-fela/withTheme)

## Tips & Tricks

### Presentational Components

While not required, we highly recommend to split your components into [presentational and container components](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.67qfcbme5).
Where container components manage the application logic, presentational components should only describe how your application is displayed (markup & styling). They get data passed as `props` and return a representation of your view for those props.
If we strictly separate our components, we actually only use Fela for presentational components.

---

## Related

- [react-fela](https://github.com/robinweser/fela/tree/master/packages/react-fela)
- [API Reference - `useFela`](api/react-fela/useFela)
- [API Reference - `RendererProvider`](api/react-fela/RendererProvider)
- [API Reference - `ThemeProvider`](api/react-fela/ThemeProvider)