sections/api/typescript.mdx
styled-components provides TypeScript definitions which empowers the editing experience in IDEs and increases type safety for TypeScript projects.
For older versions of styled-components, there are community definitions available via the
@types/styled-componentsNPM package.
TypeScript definitions for styled-components can be extended by using declaration merging since version v4.1.4 of the definitions.
So the first step is creating a declarations file. Let's name it styled.d.ts for example.
// import original module declarations
import 'styled-components';
// and extend them!
declare module 'styled-components' {
export interface DefaultTheme {
borderRadius: string;
colors: {
main: string;
secondary: string;
};
}
}
React-Native:
import 'styled-components/native'
declare module 'styled-components/native' {
export interface DefaultTheme {
borderRadius: string;
colors: {
main: string;
secondary: string;
};
}
}
DefaultTheme is being used as an interface of props.theme out of the box. By default the interface DefaultTheme is empty so that's why we need to extend it.
Now we can create a theme just by using the DefaultTheme declared at the step above.
// my-theme.ts
import { DefaultTheme } from 'styled-components';
const myTheme: DefaultTheme = {
borderRadius: '5px',
colors: {
main: 'cyan',
secondary: 'magenta',
},
};
export { myTheme };
That's it! We're able to use styled-components just by using any original import.
import styled, { createGlobalStyle, css } from 'styled-components';
// theme is now fully typed
export const MyComponent = styled.div`
color: ${props => props.theme.colors.main};
`;
// theme is also fully typed
export MyGlobalStyle = createGlobalStyle`
body {
background-color: ${props => props.theme.colors.secondary};
}
`;
// and this theme is fully typed as well
export cssHelper = css`
border: 1px solid ${props => props.theme.borderRadius};
`;
If you are adapting the styles based on props, and those props are not part of the base tag / component props, you can tell TypeScript what those extra custom props are, with type arguments like this (TypeScript v2.9+ is required):
import styled from 'styled-components';
import Header from './Header';
interface TitleProps {
readonly $isActive: boolean;
}
const Title = styled.h1<TitleProps>`
color: ${(props) => (props.$isActive ? props.theme.colors.main : props.theme.colors.secondary)};
`;
Note: if you style a standard tag (like <h1> in above example), styled-components will not pass the custom props (to avoid the Unknown Prop Warning).
However, it will pass all of them to a custom React component:
import styled from 'styled-components';
import Header from './Header';
const NewHeader = styled(Header)<{ customColor: string }>`
color: ${(props) => props.customColor};
`;
// Header will also receive props.customColor
If the customColor property should not be transferred to the Header component, you can leverage transient props, by prefixing it with a dollar sign ($):
import styled from 'styled-components';
import Header from './Header';
const NewHeader2 = styled(Header)<{ $customColor: string }>`
color: ${(props) => props.$customColor};
`;
// Header does NOT receive props.$customColor
Depending on your use case, you can achieve a similar result by extracting the custom props yourself:
import styled from 'styled-components';
import Header, { Props as HeaderProps } from './Header';
const NewHeader3 = styled(({ customColor, ...rest }: { customColor: string } & HeaderProps) => <Header {...rest} />)`
color: ${(props) => props.customColor};
`;
Or using shouldForwardProp:
import styled from 'styled-components';
import Header from './Header';
const NewHeader4 = styled(Header).withConfig({
shouldForwardProp: (prop) => !['customColor'].includes(prop),
})<{ customColor: string }>`
color: ${(props) => props.customColor};
`;
classNameWhen defining a component you will need to mark className as optional
in your Props interface:
interface LogoProps {
/* This prop is optional, since TypeScript won't know that it's passed by the wrapper */
className?: string;
}
class Logo extends React.Component<LogoProps, {}> {
render() {
return <div className={this.props.className}>Logo</div>;
}
}
const LogoStyled = styled(Logo)`
font-family: 'Helvetica';
font-weight: bold;
font-size: 1.8rem;
`;
To use function components and have typechecking for the props you'll need to define the component alongside with its type. This is not special to styled-components, this is just how React works:
interface BoxProps {
theme?: ThemeInterface;
borders?: boolean;
className?: string;
}
const Box: React.FunctionComponent<BoxProps> = (props) => <div className={props.className}>{props.children}</div>;
const StyledBox = styled(Box)`
padding: ${(props) => props.theme.lateralPadding};
`;