Using styled-components With TypeScript

Using Styled-components with React and React Native is great. Where it really shines in my opinion is when you use it with TypeScript and VS Code, getting code suggestions and errors when you write something wrong. In this article, we are going to take a look at how to use TypeScript with styled-components for better developer experience:

  1. Styled-components with VS Code and types for styled-components
  2. Theme variable suggestions using declaration merging
  3. Type checking for component props

Styled-components With VS Code

As a prerequisite, it is crucial to mention the vscode-styled-components plugin for VS Code. This plugin vastly improves the writing of styled-components with syntax highlighting, error reporting, and IntelliSense. When combined with TypeScript and styled-components, the developer experience is truly enhanced.

Styled Components doesn’t come with types so we need to install them either running:

npm i --save-dev @types/styled-components

or alternatively

yarn add @types/styled-components --dev

Theme variable suggestions using declaration merging

Wouldn’t it be cool if you get type-checking for the themes you’ve created, as well as auto-complete for the theme variables? So that when you’re writing something like this:

// The commonly seen way of accessing them
const TextOne = styled.Text`
  color: ${(props) => props.theme.primaryColor};
`

// Or by using object desctructuring for cleaner code
const TextSecond = styled.Text`
  color: ${({ theme }) => theme.primaryColor};
`

Then you wouldn’t have to remember if whether the primary color was written as primaryColor or primary_color. Instead, your IDE tells you what theme variables stored inside the theme object you’ve created.

Well good news, you can easily achieve this by using the Typescript interface merging to override the default theme that comes with styled-components (more info on how declaration merging works can be found here, and here). Here’s how to achieve that:

  1. To access the theme in our app, we need to first set up ThemeProvider and then pass down our custom theme inside it.

  2. Then we create the theme file, import the original styled-components module declaration and extend it using declaration merging. We add our own theme variables like primaryColor to the DefaultTheme and tell our theme objects to use that interface.

// App.tsx
import React from 'react'
import { ThemeProvider } from 'styled-components/native'
import Navigation from '/navigation'
import { lightTheme } from '/styles/theme'

const App = () => {
  return (
    <ThemeProvider theme={lightTheme}>
      <Navigation />
    </ThemeProvider>
  )
}

export default App

We could do this in a separate styled-components.d.ts declaration file, but placing it inside the themes.ts file allows us to tweak the theme definition at the same when we add new variables.

With the declaration merging done using the theme variables suggestions in VS Code should look like this now:

Declaration merging example

No more guessing whether it was accentColor or colorAccent.

Type-checking for custom Props

Another typical case with styled-components is using the props of the styled component to alter the styling, e.g. creating a button container that has the prop “disabled” and then using it as the conditional for reducing opacity like this:

import React from 'react'
import styled from 'styled-components/native'

type Props = {
  disabled: boolean
}

const StartButton = ({ disabled }: Props) => {
  return (
    <Button disabled={disabled}>
      <ButtonText>Start counter</ButtonText>
    </Button>
  )
}

const Button = styled.TouchableOpacity`
  opacity: ${(props) => (props.disabled ? 0.5 : 1)};
`

const ButtonText = styled.Text`
  font-size: 17px;
`

export default StartButton

This works pretty well and when we type props.something VS Code should even show us an IntelliSense suggestion for the disabled prop, as long as we’ve installed the styled-components type definition. This is because those type definitions contain the type definition for TouchableOpacity with definition for the disabled prop. However, if were to define our own prop, something such as primary which would be of boolean value we would get a type error like the below:

creenshot of a code editor with JavaScript React code showing a styled-components error. The code defines a 'StartButton' functional component with props and renders a 'Button' component with 'primary' and 'disabled' props. An error tooltip indicates a type issue: 'No overload matches this call' and 'Property 'primary' does not exist on type...'. The 'Button' styled component is defined below with an opacity style based on props, and a 'ButtonText' styled component is defined with a font size. The error message suggests issues with the 'primary' property not being recognized in the styled component's type definition.

To fix this we need to add a new type for our button which contains the primary prop and then tell styled-components to use it:

type ButtonProps = {
  primary: boolean
}

const Button = styled.TouchableOpacity<ButtonProps>`
  opacity: ${(props) => (props.primary ? 0.5 : 1)};
`

Now we should no longer get the error message unless we try to use the wrong value there. On top of that we also get autocomplete when using it to alter the styles:

Screenshot of a code editor displaying a JavaScript snippet using styled-components. There is a 'const Button' declaration for a styled TouchableOpacity with a ternary operation inside a template literal to determine its opacity. The operation reads: 'opacity: ${(props) => (props.pri... ? 0.5 : 1)};'. The autocomplete dropdown menu is suggesting 'primary' as a property, alongside other properties like 'pressRetentionOffset', 'onPressIn', and 'delayPressIn'. Below is a 'const ButtonText' declaration for a styled Text component with a 'font-size' of '17px'.