Package Exports
- tss-react
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (tss-react) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
✨ Like JSS but for TypeScript. Powered by emotion ✨
'tss-react' is intended to be a replacement for 'react-jss'.
It's API is focused on providing maximum type safety and minimum verbosity.
This module is nothing but a tinny extension for @emotion/css.
- ✅ As fast as
emotion(which is mutch fater than JSS) - ✅ As lightweight as
emotion/css. - ✅ Seamless integration with material-ui. Perfect for those who don't like the switch to Styled API in v5.
$ yarn add tss-react
#OR
$ npm install --save tss-react
Quick start
./MyComponent.tsx
import { createUseClassNames } from "./useClassNames";
const { useClassNames } = createUseClassNames<Props & { color: "red" | "blue" }>()({
(theme, { color })=> ({
"root": {
color,
"&:hover": {
"backgroundColor": "lightGrey"
}
}
})
});
function MyComponent(props: Props){
const [ color, setColor ]= useState<"red" | "blue">("red");
const { classNames }=useClassNames({...props, color });
return (
<span className={classNames.root}>
hello world
</span>
);
}./useClassNames.ts
import { createUseClassNamesFactory } from "tss-react";
function useTheme(){
return {
"primaryColor": "blue";
};
}
// material-ui users can pass in useTheme imported like: import { useTheme } from "@material-ui/core/styles"
export const { createUseClassNames } = createUseClassNamesFactory({ useTheme });Material-UI users only, don't forget to enable injectFirst
import reactDom from "react-dom";
import { StylesProvider } from "@material-ui/core/styles";
reactDom.render(
<StylesProvider injectFirst>
<Root/>,
</StylesProvider>,
document.getElementById("root")
);
Why this instead of react-jss or the hook API of Material UI v4's styling solution?
Let's see what's wrong with react-jss:
import { makeStyles, createStyles } from "@material-ui/core/styles";
type Props = {
color: "red" | "blue";
};
const useStyles = makeStyles(
theme => createStyles<"root" | "label">, Props>({
"root": {
"backgroundColor": theme.palette.primary.main
},
"label": ({ color })=>({
color
})
})
);
function MyComponent(props: Props){
const classes = useStyles(props);
return (
<div className={classes.root}>
<span className={classes.label}>
Hello World
</span>
</div>
);
}Many pain points:
- Because TypeScript doesn't support partial argument inference,
we have to explicitly enumerate the classes name as an union type
"root" | "label". - We shouldn't have to import
createStylesto get correct typings. - Inconsistent naming conventions
makeStyles -> useStyles -> classes
Let's now compare with tss-react
import { createUseClassNames } from "./useClassNames";
type Props = {
color: "red" | "blue";
};
const { useClassNames } = createUseClassNames<Props>()(
(theme, { color })=> ({
"root": {
"backgroundColor": theme.palette.primary.main
},
"label": { color }
})
);
function MyComponent(props: Props){
const { classNames } = useClassNames(props);
return (
<div className={classNames.root}>
<span className={classNames.label}>
Hello World
</span>
</div>
);
}Benefits:
- Less verbose, same type safety.
- Consistent naming convention
createUseClassNames -> useClassNames -> classNames. - You don't need to remember how things are supposed to be named, just let intellisense guide you.
Besides, JSS, at least the version bundled into material-ui, have other problems:
Why this instead of Styled component ?
See this issue
How to avoid bundling in another copy of @emotion/css
Internally this module makes use of the css function from @emotion/css.
If you already have @emotion/css as dependency and want to make sure not
to bundle @emotion/css twice you can provide your own copy of css():
./useClassNames.ts
import { createUseClassNamesFactory } from "tss-react/createUseClassNamesFactory";
import { css } from "@emotion/css";
...
export const { createUseClassNames } = createUseClassNamesFactory({ useTheme, css });API References
createUseClassNamesFactory()- Direct re-export of
@emotion/css
The three function that you should end up using the most are css any cx, reexported from @emotion/css, and
createUseClassNamesFactory, the only function that this module actually implement.
Consider this example to understand how css, cx and useClassNames are supposed to be
used together:
MyButton.tsx
import { createUseClassNames } from "./useClassNames";
import { cx, css } from "tss-react";
export type Props ={
text: string;
onClick(): void;
isDangerous: boolean;
className?: string;
};
const { useClassesNames } = createUseClassNames<Props>()(
(theme, { color })=>({
"root": {
"backgroundColor": isDangerous ? "red" : "grey"
}
})
);
export function MyButton(props: Props){
const { className, onClick, text } = props;
const { classesNames } = useClassesNames(props);
return (
<button
//You want to apply the styles in this order
//because the parent should be able ( with
//the className prop) to overwrite the internal
//styles ( classesNames.root )
className={cx(classesNames.root, className)}
onClick={onClick}
>
{text}
</button>
);
}App.tsx
import { css } from "tss-react";
function App(){
return (
<MyButton
//The css function return a className, it let you
//apply style directly on in the structure without
//having to use createUseClassNames
className={css({ "margin": 40 })}
text="click me!"
isDangerous={false}
onClick={()=> console.log("click!")}
/>
);
}