Skip to content

Instantly share code, notes, and snippets.

@kof
Last active March 22, 2017 18:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kof/3277ee04d2100bab3e6c6c33656edce7 to your computer and use it in GitHub Desktop.
Save kof/3277ee04d2100bab3e6c6c33656edce7 to your computer and use it in GitHub Desktop.
Styled Primitive Components with JSS
import injectSheet, {jss} from 'react-jss'
export function createStyled(options) {
const sheet = jss.createStyleSheet(null, options)
return function styled(tag, style) {
const {className} = sheet.addRule(Math.random(), style)
const StyledComponent = props => createElement(tag, {className, ...props})
return injectSheet(sheet)(StyledComponent)
}
}
export const styled = createStyled()
import {styled} from 'react-jss-components'
const RedButton = styled('button', {
color: 'red'
})
<RedButton>Click me</RedButton>
@kof
Copy link
Author

kof commented Oct 21, 2016

The idea (pseudo code) is to create styled primitive components, similar to https://github.com/styled-components/styled-components but using jss.

@kof
Copy link
Author

kof commented Oct 22, 2016

Interesting observations about this approach:

  • No HOC around the entire user component, which means public API of user component remains untouched.
  • When styled components are created at the top level of a module (same like import statements), JSS only creates virtual rules. All rules get rendered when a first primitive is about to render. It is a buffering for free.
  • There are 2 options, use one global styled() function or create it for every file or user component with createStyled(). If first option is used, all rules for all primitives are virtually created and will render in one style tag once first primitive renders. If using the second option, styles are separated on per-component/file basis and will render also when a first primitive of that component renders.

@kof
Copy link
Author

kof commented Oct 22, 2016

Good point by @nathanmarks:
For SSR we need to detect when a new request comes in. We can achieve that by wrapping the entire application into a JssProvider and either setting something on a context that tells our component that new request was created or provider creates a new jss instance and we just use it within the styled() fn.

@sergeysova
Copy link

Work example:

import React, { PropTypes } from 'react'
import cn from 'classnames'

const domElements = ['a', 'button', 'div', 'span', 'h1'] // etc



export default function createStyledJss(injectSheet) {
  const styledJss = (tag, name, styles) => {
    const themedStyles = { [name]: styles() }
    const sheetInjector = injectSheet(themedStyles)

    const component = ({ sheet, classes, className, ...props }) => (
      React.createElement(tag, { ...props, className: cn(classes[name], className) })
    )
    const styledComponent = sheetInjector(component)
    styledComponent.displayName = name

    return styledComponent
  }

  domElements.forEach(tag => {
    styledJss[tag] = styledJss.bind(null, tag)
  })

  return styledJss
}

Usage:

// app/styled.js

import { create as createJss } from 'jss'
import { create as createInjectSheet } from 'react-jss'
import presetDefault from 'jss-preset-default'
import createStyledJss from 'jss-styled'

export const jss = createJss()

jss.setup(presetDefault())

export const injectSheet = createInjectSheet(jss)
export default createStyledJss(injectSheet)


// app/components/button.js

import React from 'react'
import styled from '../styled'

const Button = styled.button('Button', () => ({
  backgroundColor: 'black',
  padding: 20,
  color: 'white',
}))

export default Button

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment