styled-jsx: Can no longer inject style rule from a const string since version 2

It seems impossible to inject a style rule from a const string using styled-jsx version 2. Since this feature was supported in version 1, I would consider this a regression.

My personal use case is very simple, I would like to be able to define styles in CSS files to benefit from standard tooling for editing/linting, which are much harder to leverage with inline CSS-in-string or CSS-in-template-literal. I am sure that many other use cases would also be a fit.

Is this a bug? A known issue/limitation? I’d be happy to help either way, provided a little guidance.

Simple demonstration

You can also run a simple use case from this sample project (using Next.js): https://github.com/coox/styled-jsx-unexpected-curly-braces/blob/master/pages/index.js

Inline string: works!

The following rule injection works great, as expected:

<style jsx global>{'body { background-color: green; }'}</style>

Output:

<style id="__jsx-id">body{background-color:green;}</style>

Constant string literal: breaks!

However, just moving the string literal to a constant does not even build:

const greenBodyStyleSheet = 'body { background-color: green; }';
<style jsx global>{greenBodyStyleSheet}</style>

Output:

SyntaxError: The Identifier `greenBodyStyleSheet` is either `undefined` or it is not an external StyleSheet reference i.e. it doesn't come from an `import` or `require` statement`

Interpolated constant string in template literal: puzzles!

The closest I got was trying to interpolate the const inside a template literal. This does build, but the output was not what I was expecting:

const greenBodyStyleSheet = 'body { background-color: green; }';
<style jsx global>{`${greenBodyStyleSheet}`}</style>

Output (notice the unexpected surrounding curly braces):

<style id="__jsx-id">{body { background-color: green; };}</style>

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 4
  • Comments: 26 (3 by maintainers)

Most upvoted comments

It was’t obvious for me when I started using styled-jsx that the build process you describe is pretty much the only way to achieve what I was trying to do… But thanks to this discussion, and with inspiration from Next.js’ with-global-stylesheet example, I managed to adapt your hypothetical build script as a webpack loader.

Now the following is possible, and what’s more, with hot module reload goodness!

import styleSheet from './page.css';

<style jsx global>{styleSheet}</style>

I have updated my demo repo accordingly.

The principle is to leverage Webpack (in my case, through next.config.js) to load CSS files as JavaScript modules exposing the style in a css-tagged template literal.

This involves a combination of:

As far as I’m concerned, the issue documents ways to address style injection from an outside file, and can certainly be closed.

I might want to publish this simple loader (edit: done!), this could prove useful! Also, I might want to submit this as a Next.js example.

Thanks a lot for your help and explanations, @giuseppeg.

I finally upgraded my styled-jsx and implemented the following to load styles from dependencies:

import Styles from 'library/css/styles.min.css'
...
<Head><style dangerouslySetInnerHTML={{ __html: Styles }} /></Head>

I just realized that css template literal is responsible for generating the hash…but

css`${tachyons}`

Leads to extra brackets as mentioned by @coox, if the style is imported using global.

I could come up with a more elaborate build process as suggested on this thread, but that would defeat the purpose of keeping it simple…

Hi @giuseppeg, this is a slightly different scenario than @coox’s situation, but kind of related and therefore I’d like your 👀 .

Background: Use inline-import babel plugin to import a CSS file into a string, and then stick it inside <style jsx global>

I added an example on next.js to load a CSS file located in node_modules: https://github.com/zeit/next.js/pull/3157

This uses inline-import babel plugin, which allows you to import a CSS file into a string, and is based off a comment from here: https://github.com/zeit/next.js/issues/544#issuecomment-325512576 - a good use case is to quickly import libraries like normalize.css or other CSS libraries, without having to do extra work outlined in the previous with-global-stylesheet example.

From the README on my PR

  • Install babel-plugin-inline-import using npm or yarn
  • Then, add this to your .babelrc:
{
  "plugins": [
    [
      "inline-import",
      {
        "extensions": [".css"]
      }
    ]
  ],
  "presets": ["next/babel"],
  "ignore": []
}
  • Install any CSS library using npm or yarn. In this example, I installed tachyons.
  • Import the CSS file. Here, I’m importing a CSS file located at node_modules/tachyons/css/tachyons.min.css.
import tachyons from 'tachyons/css/tachyons.min.css'
  • Add it globally using styled-jsx:
<style jsx global>{tachyons}</style>

Problem: It generates __jsx-undefined ID on style tag

But I’m not sure if this is considered as a hack and should be avoided. One reason I’m worried is that, if I run the above example, everything works perfectly except that the id of the generated style tag is __jsx-undefined:

<style id="__jsx-undefined">...</style>

Failed Attempts

Also, instead of

<style jsx global>{tachyons}</style>

If I do

<style jsx global>{`${tachyons}`}</style>`

It generates a non-undefined id on the style tag, but it generates extra curly braces at the beginning and at the end and therefore fails.

It’d be great if you could take a look. It’s merged to master on next: https://github.com/zeit/next.js/tree/master/examples/with-global-stylesheet-simple