Black Lives Matter.

Emotion includes TypeScript definitions for @emotion/react and @emotion/styled. These definitions also infer types for css properties with the object syntax, HTML/SVG tag names, and prop types.

@emotion/react

import { css } from '@emotion/react'

const titleStyle = css({
  boxSizing: 'border-box',
  width: 300,
  height: 200
})

const subtitleStyle = css`
  box-sizing: border-box;
  width: 100px;
  height: 60px;
`

TypeScript checks css properties with the object style syntax using csstype package, so following code will emit errors.

import { css } from '@emotion/react';

const titleStyle = css({
                       ^ Argument of type 'boxSizing: 'bordre-box';' is not assignable [...]
  boxSizing: 'bordre-box', // Oops, there's a typo!
  width: 300,
  height: 200,
});

To make the css prop work with pure TypeScript (without babel plugin) you need to add /** @jsx jsx */ at the top of every file that is using the css prop:

/** @jsx jsx */
import { jsx } from '@emotion/react'

<div css={{ background: 'black' }} />

As a result you may be not able to use react fragment shorthand syntax - <></>, but still you can use <Fragment></Fragment>. This is a limitation of the TypeScript compiler not being able to independently specify jsx pragma and jsxFrag pragma.

You can still use the css helper and pass the className yourself (ensure you are importing from the @emotion package, not @emotion/react).

import { css } from '@emotion'

<div className={css({ background: 'black' })} />

css prop

When using our jsx pragma the support for css prop is being added only for components that accepts className prop, as our jsx factory function takes provided css prop, resolves it and pass the generated className to the rendered component.

However, it’s not possible to leverage css prop support being added conditionally based on a type of rendered component when one is not using our jsx pragma. For those cases when people use our pragma implicitly (for example when using our @emotion/babel-preset-css-prop) we have a special file that can be imported once to add support for the css prop globally, for all components. Use it like this:

import {} from '@emotion/react/types/css-prop'

@emotion/styled

HTML/SVG elements

import styled from '@emotion/styled'

const Link = styled('a')`
  color: red;
`

const Icon = styled('svg')`
  stroke: green;
`

const App = () => <Link href="#">Click me</Link>
import styled from '@emotion/styled';

const NotALink = styled('div')`
  color: red;
`;

const App = () => (
  <NotALink href="#">Click me</NotALink>
            ^^^^^^^^ Property 'href' does not exist [...]
);

withComponent

import styled from '@emotion/styled'

const NotALink = styled('div')`
  color: red;
`

const Link = NotALink.withComponent('a')

const App = () => <Link href="#">Click me</Link>

// No errors!

Passing Props

You can type the props of styled components.

import styled from '@emotion/styled'

type ImageProps = {
  src: string
  width: number
}

// Using a css block
const Image0 = styled.div<ImageProps>`
  width: ${props => props.width};
  background: url(${props => props.src}) center center;
  background-size: contain;
`
const Image0 = styled('div')<ImageProps>`
  width: ${props => props.width};
  background: url(${props => props.src}) center center;
  background-size: contain;
`

// Or with object styles
const Image1 = styled('div')<ImageProps>(
  {
    backgroundSize: 'contain'
  },
  props => ({
    width: props.width,
    background: `url(${props.src}) center center`
  })
)

React Components

Emotion can also style React components and will infer component props as expected.

import React, { FC } from 'react'
import styled from '@emotion/styled'

interface ComponentProps {
  className?: string
  label: string
}

const Component: FC<ComponentProps> = ({
  label,
  className
}) => <div className={className}>{label}</div>

const StyledComponent0 = styled(Component)`
  color: ${props => label === 'Important' ? 'red' : 'green'};
`

const StyledComponent1 = styled(Component)({
  color: 'red'
})

const App = () => (
  <div>
    <StyledComponent0 label="Important" />
    <StyledComponent1 label="Yea! No need to re-type this label prop." />
  </div>
)

Forwarding props

Sometimes you want to wrap an existing component and override the type of a prop. Emotion allows you to specify a shouldForwardProp hook to filter properties which should be passed to the wrapped component.

If you make should shouldForwardProp a type guard then only the props from the type guard will be exposed.

For example:

const Original: React.FC<{ prop1: string, prop2: string }> = () => null

interface StyledOriginalExtraProps {
  // This prop would conflict with the `prop2` on Original
  prop2: number
}
const StyledOriginal = styled(Original, {
  // Filter prop2 by only forwarding prop1
  shouldForwardProp: (propName): propName is 'prop1' => propName === 'prop1'
})<StyledOriginalExtraProps>(props => {
  // props.prop2 will be `number`
  return {}
})

// No more type conflict error
;<StyledOriginal prop1="1" prop2={2} />

Passing props when styling a React component

import React, { FC } from 'react'
import styled from '@emotion/styled'

interface ComponentProps {
  className?: string
  label: string
}

const Component: FC<ComponentProps> = ({
  label,
  className
}) => <div className={className}>{label}</div>

interface StyledComponentProps {
  bgColor: string
}

const StyledComponent0 = styled(Component)<StyledComponentProps>`
  color: red;
  background: ${props => props.label ? props.bgColor : 'white'};
`
// or
const StyledComponent1 = styled(Component)<StyledComponentProps>(
  props => ({
    color: 'red'
    background: props.label ? props.bgColor : 'white'
  })
)

const App = () => (
  <div>
    <StyledComponent0
      bgColor="red"
      label="Some cool text"
    />
    <StyledComponent1
      bgColor="red"
      label="Some more cool text"
    />
  </div>
)

Define a Theme

By default, props.theme is an empty object because it’s the only thing that is type-safe as a default. You can define a theme type by extending our type declarations via your own declarations file.

emotion.d.ts

import '@emotion/react'

declare module '@emotion/react' {
  export interface Theme {
    color: {
      primary: string
      positive: string
      negative: string
    }
  }
}

// You are also able to use a 3rd party theme this way:
import '@emotion/react'
import { MuiTheme } from 'material-ui'

declare module '@emotion/react' {
  export interface Theme extends MuiTheme {}
}

Button.tsx

import styled from '@emotion/styled'

const Button = styled('button')`
  padding: 20px;
  background-color: ${props => props.theme.primary};
  border-radius: 3px;
`

export default Button

If you were previously relying on theme being an any type, you have to restore compatibility with:

emotion.d.ts

import '@emotion/react'

declare module '@emotion/react' {
    export interface Theme extends Record<string, any> {}
}

TypeScript < 2.9

For Typescript <2.9, the generic type version only works with object styles due to https://github.com/Microsoft/TypeScript/issues/11947.

You can work around this by specifying the prop types in your style callback:

const StyledComponent0 = styled(Component)`
  color: red;
  background: ${(props: StyledComponentProps) =>
    props.bgColor};
`

NOTE: This approach you will have to perform the intersection with the component props yourself to get at the component props

const StyledComponent0 = styled(Component)`
  color: red;
  background: ${(props: StyledComponentProps & ComponentProps) =>
    props.bgColor};
`