TSS-React: Type-Safe CSS-in-JS with Emotion
tss-react is a robust CSS-in-JS solution providing a type-safe API inspired by `react-jss`, but built on top of Emotion. It offers seamless integration with Material UI, supports Next.js App and Page Router environments (though not Server Components due to dynamic style generation), and enables dynamic style generation based on component props and states using plain CSS. Currently at version 4.9.20, it receives frequent patch and minor updates, indicating active development and maintenance. Key differentiators include its strong typing, a JSS-like API for developers familiar with it, the ability to isolate styles from JSX for cleaner component structures, and features like arbitrary specificity increase and a type-safe equivalent of JSS's nested selectors. It is presented as an advantageous replacement for `@material-ui v4 makeStyles` and `react-jss`, providing a maintained solution for dynamic styling with a minimal impact on bundle size.
Common errors
-
TypeError: Cannot read properties of undefined (reading 'main') (or similar theme property access error)
cause The theme object is not being correctly provided to `tss-react` via Emotion's `ThemeProvider`, or the theme structure is different than expected.fixEnsure your component tree is wrapped with `import { ThemeProvider } from '@emotion/react';` and that a valid theme object is passed to it. If using Material UI, ensure the MUI theme is also accessible to Emotion's context. -
Error: Invalid hook call. Hooks can only be called inside of the body of a functional component.
cause The `makeStyles` or `useStyles` hook is being called outside of a React functional component or custom hook context.fixEnsure `makeStyles` is called at the module level to create your `useStyles` hook, and that the `useStyles` hook itself is called within a functional component or another custom hook. -
Cannot find module 'tss-react' or its corresponding type declarations.
cause The package is not installed, or there's a problem with module resolution in your build system (e.g., incorrect `paths` in `tsconfig.json`, or a CJS/ESM mismatch).fixRun `npm install tss-react` or `yarn add tss-react`. For TypeScript, ensure your `tsconfig.json` targets a modern module system like `esnext` and check `moduleResolution` settings. Avoid `require()` for importing tss-react. -
Type 'Classes' is not assignable to type 'string | undefined'. Property 'root' is missing in type 'Classes'.
cause A type-checking error indicating that your style definition is incomplete or incorrect, or you are trying to use a class name that hasn't been defined in your `makeStyles` object.fixVerify that all class names you are trying to use (e.g., `classes.root`) are actually defined as keys in the object returned by your `makeStyles` function. Ensure your styles object adheres to the expected CSS properties and types.
Warnings
- gotcha tss-react does not support React Server Components (RSC) in Next.js. Dynamic style generation, a core feature, is incompatible with the RSC paradigm. Users needing RSC support should consider zero-runtime CSS solutions.
- breaking The `makeStyles` API expects a theme object to be provided via `@emotion/react`'s `ThemeProvider`. If `tss-react` is used alongside `@mui/material`, ensure both Material UI's `ThemeProvider` and Emotion's `ThemeProvider` are correctly configured, typically wrapping the Material UI theme.
- gotcha While `tss-react` is heavily inspired by JSS, it uses Emotion under the hood. Developers familiar with JSS might expect certain behaviors or plugins not directly available or implemented differently in Emotion.
- breaking React 19 compatibility was added in `v4.9.18`. Older versions of `tss-react` may not function correctly or might display warnings when used with React 19.
- breaking Material UI v7 support was added in `v4.9.16`. Using `tss-react` with Material UI v7 without this update may lead to peer dependency warnings or runtime issues.
Install
-
npm install tss-react -
yarn add tss-react -
pnpm add tss-react
Imports
- makeStyles
const { makeStyles } = require('tss-react')import { makeStyles } from 'tss-react' - useStyles
const classes = useStyles()
const { classes } = useStyles() - withStyles
import { withStyles } from 'tss-react'import { withStyles } from 'tss-react/mui' - useTheme
import { useTheme } from 'tss-react'import { useTheme } from '@emotion/react'
Quickstart
import React from 'react';
import { makeStyles } from 'tss-react';
interface MyComponentProps {
variant?: 'primary' | 'secondary';
isActive?: boolean;
}
const useStyles = makeStyles<MyComponentProps>()(
(theme, { variant, isActive }) => ({
container: {
padding: '16px',
backgroundColor: variant === 'primary' ? 'lightblue' : 'lightgray',
borderRadius: '8px',
border: isActive ? '2px solid blue' : 'none',
'&:hover': {
boxShadow: '0px 0px 8px rgba(0,0,0,0.2)'
}
},
title: {
color: theme.palette?.primary?.main || 'navy',
fontSize: '24px',
marginBottom: '8px'
},
description: {
color: 'darkslategray',
fontSize: '16px'
}
})
);
function MyComponent({ variant = 'primary', isActive = false }: MyComponentProps) {
const { classes, cx, theme } = useStyles({ variant, isActive });
return (
<div className={cx(classes.container, { 'active-state': isActive })}>
<h2 className={classes.title}>Hello from TSS-React!</h2>
<p className={classes.description}>This component demonstrates dynamic styling with props and Emotion theme access.</p>
<p>Current theme primary color: {theme.palette?.primary?.main || 'N/A'}</p>
</div>
);
}
// To run this, you'd typically wrap it in an Emotion `ThemeProvider`
// and a Material-UI `ThemeProvider` if using `@mui/material`.
// Example usage:
// function App() {
// const muiTheme = createTheme({
// palette: { primary: { main: '#1976d2' } }
// });
// return (
// <MuiThemeProvider theme={muiTheme}>
// <EmotionThemeProvider theme={muiTheme}>
// <MyComponent variant="secondary" isActive={true} />
// </EmotionThemeProvider>
// </MuiThemeProvider>
// );
// }
export default MyComponent;