JSS v10
A long-awaited stable version of JSS v10 is out and here is a summary of what happened. This is a higher-level summary, a full changelog is here and a migration guide here.
It took us almost a year to release this version with much fewer features and improvements than originally planned, because most of the work happens in our spare time and current Open Collective status isn’t usable to pay contributors, not to mention myself.
New hooks-based API for React-JSS
The problem with HOCs besides of some level of indirection is also performance cost. In the end, a HOC needs to generate an additional component, which still needs to add up to React’s reconciliation work. Also, HOCs are not nice to look at in dev tools. Another bonus is also that this new interface simplifies the usage with type systems like Flowtype or TypeScript.
With hooks API, these problems are “gone”, but there is more to it. The new interface gives you better control over data that is passed to the function values.
import React from 'react'
import {createUseStyles} from 'react-jss'const useStyles = createUseStyles({
myButton: {
padding: props => props.spacing
},
myLabel: props => ({
display: 'block',
color: props.labelColor,
fontWeight: props.fontWeight,
fontStyle: props.fontStyle
})
})const Button = ({children, ...props}) => {
const classes = useStyles(props)
return (
<button className={classes.myButton}>
<span className={classes.myLabel}>{children}</span>
</button>
)
}
So why do we need a hook creator function createUseStyles()
that returns a hook function useStyles()
?
It’s because we want to automate managing Style Sheets for you by integrating them with React’s lifecycle. When component umounts, it removes unused styles from the DOM and keeps the long-term application performance consistent, because CSS attached to the Render Tree is not free of charge.
Notice that you can call useStyles(data)
with any data argument, so you have a chance to decide what data goes into function values. For example, you could as well use state
or context
.
In addition, it gives us better control over Theming:
import React from 'react'
import {createUseStyles, useTheme} from 'react-jss'const useStyles = createUseStyles({
myButton: {
padding: ({theme}) => theme.spacing
}
})const Button = ({children, ...props}) => {
const theme = useTheme()
const classes = useStyles({...props, theme})
return (
<button className={classes.myButton}>Test</button>
)
}
Notice that you could also use the theme
in your rendering logic or pass anything else as a theme
.
Scoped keyframes animations
Prior to v10, keyframe IDs were not generated. They were as global as regular CSS classes are. This can be a source of bugs and frustration because they can conflict and override each other in an unpredictable manner.
In v10, keyframe IDs have a generated suffix, using the same generateId()
function that is used for class names generation and makes sure they are unique. You can still have a global keyframe ID using @global
rule.
The new syntax looks like this:
const styles = {
'@keyframes slideRight': {
from: {opacity: 0},
to: {opacity: 1}
},
container: {
animationName: '$slideRight'
}
}
and compiles to
@keyframes keyframes-slideRight-0 {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.container-1 {
animation-name: keyframes-slideRight-0;
}
To get the generated keyframes id from JSS StyleSheet, you can use sheet.keyframes.slideRight
.
The full syntax for function values and observables
Prior to v10, function values were not processed by plugins, so not all syntax extensions have been applied. This was mainly to optimize the performance of function values. As you probably know JSS aims to use functions to not only express values which depend on runtime data like props
, but also to implement it in such a way, that allows those functions to update values at 60FPS.
I was hesitant adding full plugins processing to those functions because this will impact the update performance for high-frequency updates like stateful animations.
The solution we ended up with is to make them support full syntax by default (things like media queries, nesting, etc.), but to enable opting out in case you want to reduce the overhead to the minimum:
sheet.update(data, {process: false})
Houdini Typed CSSOM
For those who are unfamiliar with Houdini efforts, check out this and this. Current implementation status can be tracked here.
How is it useful in JSS? It gives you a way to optimize the update performance of CSS values. The point of using Typed CSSOM objects is to be able to change a value without involving a CSS parser.
Here is a simple example that updates a Typed CSSOM value, in a nutshell, it’s this:
const styles = {
button: {
top: data => data.top || 0
}
}// Create an instance of CSSUnitValue.
const top = CSS.px(20);// Mutate the value without CSS parsing.
top.value = top.value + 20;Tell JSS to set the updated value.
update({top})
Monorepo
We had troubles maintaining multi-repo setup because JSS has a lot of plugins and other packages. Keeping the dependencies and configs up-to-date has become more and more work and slowed us down.
Along with migrating to a monorepo, we decided to use a single version for all packages. Figuring out compatible versions of dependencies has become a source of bugs and confusion.
ESM ready
All JSS packages are published in CommonJS and ESM format, which means JSS takes less space in your bundle depending on which API’s you are using. Make sure to use a bundler that supports tree-shaking.
TypeScript Types
All packages come with TS definitions now, we are aiming to provide better TypeScript support, but of course, your contributions are welcome.
Final Notes
JSS is an Open Source tool. If your company is for-profit, depends on JSS and you expect the tool to become better — contribute with quality bug reports, documentation, code or support us on Open Collective.
Also a big thanks to BrowserStack for providing a free account, since CSSOM has many cross-browser issues and it wouldn’t be possible to do what we did without testing it in real browsers.