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 optimized for TypeScript. Powered by emotion ✨
'tss-react' is intended to be a replacement for 'react-jss' and for
@material-ui v4 makeStyle.
It's API is focused on providing maximum type safety and minimum verbosity.
This module is a tinny extension for @emotion/react.
- ✅ As fast as
emotion(which is much faster than mui'smakeStyles) - ✅ As lightweight as
emotion/react. - ✅ Server side rendering support (e.g: Next.js).
- ✅ Seamless integration with material-ui v5. Perfect for those who don't like the switch from the Hook API to the Styled API in v5.
$ yarn add tss-react
Quick start
./makeStyles.ts
import { createMakeStyless } from "tss-react";
function useTheme(){
return {
"primaryColor": "#32CD32";
};
}
// material-ui users can pass in useTheme imported like: import { useTheme } from "@material-ui/core/styles";
export const { makeStyles } = createMakeStyles({ useTheme });./MyComponent.tsx
import { makeStyles } from "./makeStyles";
const { useStyles } = makeStyles<{ color: "red" | "blue" }>()({
(theme, { color })=> ({
"root": {
color,
"&:hover": {
"backgroundColor": theme.primaryColor
}
}
})
});
function MyComponent(props: Props){
const { className } = props;
const [ color, setColor ]= useState<"red" | "blue">("red");
const { classes, cx }=useStyles({ color });
return (
<span className={cx(classes.root, className)}>
hello world
</span>
);
}Material-UI users only, don't forget to enable injectFirst
import { render } from "react-dom";
import { StylesProvider } from "@material-ui/core/styles";
render(
<StylesProvider injectFirst>
<Root />,
</StylesProvider>,
document.getElementById("root"),
);API documentation
Exposed APIs
import {
createMakeStyles, //<- Create an instance of makeStyle() for your theme.
css, //<- The function as defined in @emotion/css https://emotion.sh
cx, //<- The function as defined in @emotion/css
keyframe, //<- The function as defined in @emotion/react and @emotion/css
GlobalStyles, //<- A component to define global styles.
} from "tss-react";makeStyles()
Your component style may depend on the props and state of the components:
const { useStyles } = makeStyles<{ color: string; }>()({
(_theme, { color })=> ({
"root": {
"backgroundColor": color
}
})
});
//...
const { classes } = useStyles({ "color": "grey" });...Or it may not:
const { useStyles } = makeStyles()({
//If you don't need neither the theme nor any state or
//props to describe your component style you can pass-in
//an object instead of a callback.
"root": {
"backgroundColor": "pink",
},
});
//...
const { classes } = useStyles();useStyle()
Beside the classes, useStyles also returns cx, css and your theme.
const { classes, cx, css, theme } = useStyles(/*...*/);cx and css cans be imported directly from tss-react but it's convenient to
be able to access them here.
<GlobalStyles />
Sometimes you might want to insert global css.
You can use the <GlobalStyles /> component to do this.
It's children should be of same type as the css() function
argument.
import { GlobalStyles } from "tss-react";
function MyComponent() {
return (
<>
<GlobalStyles
styles={{
"body": {
"backgroundColor": "pink",
},
".foo": {
"color": "cyan",
},
}}
/>
<h1 className="foo">This text will be cyan</h1>
</>
);
}Server Side Rendering (SSR)
There are some minimal configuration required to make tss-react
work with SSR.
With Next.js
If you don't have a _document.tsx
Just create a file page/_document.tsx as follow:
import { Document } from "tss-react/nextJs";
export default Document;Or, if have have a _document.tsx but you haven't overloaded getInitialProps
import Document from "next/document";
import type { DocumentContext } from "next/document";
import { getInitialProps } from "tss-react/nextJs";
export default class AppDocument extends Document {
static async getInitialProps(ctx: DocumentContext) {
return getInitialProps(ctx);
}
//...Rest of your class...
}Or, if have have a _document.tsx and an overloaded getInitialProps
import Document from "next/document";
import type { DocumentContext } from "next/document";
import { pageHtmlToStyleTags } from "tss-react/nextJs";
export default class AppDocument extends Document {
static async getInitialProps(ctx: DocumentContext) {
const page = await ctx.renderPage();
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
"styles": (
<>
{initialProps.styles}
{pageHtmlToStyleTags({ "pageHtml": page.html })}
</>
),
};
}
//...Rest of your class...
}With any other framework
import { renderToString } from "react-dom/server";
import createEmotionServer from "@emotion/server/create-instance";
import { cache } from "tss-react/cache";
import { createMakeStyles } from "tss-react";
const { extractCriticalToChunks, constructStyleTagsFromChunks } =
createEmotionServer(cache);
function useTheme() {
return {
"primaryColor": "#32CD32",
};
}
const { TssProviderForSsr, makeStyles, useStyleTools } = createMakeStyles({
useTheme,
});
export { makeStyles, useStyleTools };
const element = <App />;
const { html, styles } = extractCriticalToChunks(renderToString(element));
res.status(200).header("Content-Type", "text/html").send(`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>My site</title>
${constructStyleTagsFromChunks({ html, styles })}
</head>
<body>
<div id="root">${html}</div>
<script src="./bundle.js"></script>
</body>
</html>`);Development
yarn
yarn build
#For automatically recompiling when file change
#npx tsc -w
# To start the Single Page Application test app (create react app)
yarn start_spa
# To start the Server Side Rendering app (next.js)
yarn start_ssrIn SSR everything should work with JavaScript disabled
Road map to v1:
FAQ
Click to expand
Why this instead of the hook API of Material UI v4?
First of all because makeStyle is deprecated in @material-ui v5 but also
because it has some major flaws. Let's consider this example:
import { makeStyles, createStyles } from "@material-ui/core/styles";
type Props = {
color: "red" | "blue";
};
const useStyles = makeStyles(
theme => createStyles<"root" | "label">, { color: "red" | "blue"; }>({
"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>
);
}Two 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.
Let's now compare with tss-react
import { makeStyles } from "./makeStyles";
type Props = {
color: "red" | "blue";
};
const { useStyles } = makeStyles<{ color: "red" | "blue" }>()(
(theme, { color }) => ({
"root": {
"backgroundColor": theme.palette.primary.main,
},
"label": { color },
}),
);
function MyComponent(props: Props) {
const { classes } = useStyles(props);
return (
<div className={classes.root}>
<span className={classes.label}>Hello World</span>
</div>
);
}Benefits:
- Less verbose, same type safety.
- You don't need to remember how things are supposed to be named, just let intellisense guide you.
Besides, the hook api of material-ui, have other problems:
Why this instead of Styled component ?
See this issue