├── .gitignore ├── README.md ├── example ├── Button.js ├── app.js ├── index.html └── package.json ├── index.js ├── package.json └── prod.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | bundle.js 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Keep your CSS with your React component definitions 2 | 3 | I've recently been creating a large client side JavaScript application with React and Flux. As of right now the app has 35 basic React components, about 10 of which have their own custom CSS. I was in the bad habit of simply appending CSS for new components to a global stylesheet as I built them, but I knew this approach wouldn't be maintainable in the longer term. 4 | 5 | What I really wanted was to keep my CSS in the same file as my component definition so I wouldn't need to go fishing around in my global stylesheet every time I wanted to make a change. This would also mean that CSS would only be included if the component was require'd in the project somewhere. Removing a component from a project would remove its corresponding CSS automatically - no need to remember to remove it from the global stylesheet. 6 | 7 | Enough background - here's what it looks like: 8 | 9 | var React = require('react'); 10 | var Button = React.createClass({ 11 | render: function() { 12 | return ( 13 |
My Button
14 | ) 15 | } 16 | }); 17 | 18 | require('react-styl')(` 19 | .btn { 20 | display: inline-block; 21 | background: blue; 22 | padding: 5px; 23 | } 24 | .btn:hover { 25 | background: red; 26 | } 27 | `) 28 | 29 | module.exports = Button; 30 | 31 | You'll notice we use the convenience of ES6 multiline strings (`) so we can write css directly in our component JS files without any funky quoting. You'll need to run this through an ES6->ES5 converter (such as es6ify) for your resulting code to be cross-browser compatible. 32 | 33 | Now, to render the CSS on the client, simply add a line to your bootstrap file. Something like: 34 | 35 | React.render(, document.getElementById('react')); 36 | require('react-styl').addStylesheet(); 37 | 38 | That's it! But how does it work? Here's the (slimmed down) code for the react-styl module: 39 | 40 | var _styles = ""; 41 | 42 | module.exports = function(styles) { 43 | _styles += styles; 44 | } 45 | 46 | module.exports.addStylesheet = function() { 47 | var style = document.createElement('style'); 48 | style.type = 'text/css'; 49 | style.appendChild(document.createTextNode(_styles)); 50 | document.head.appendChild(style); 51 | } 52 | 53 | Every call to require('react-styl')('css string') appends the given css to the _styles variable. Since each component is cached after being require'd somewhere in your project, component CSS is only appended to the _styles variable once, even though your component may be require'd many times throughout your project. Then it's simply a case of appending the resulting CSS to a style tag and you're done. 54 | 55 | If you want to use a CSS preprocessor, like styl, simply edit your addStylesheet function to process the css before it gets added to the style tag. Something like this: 56 | 57 | var styl = require('styl'); 58 | module.exports.addStylesheet = function() { 59 | var css = styl(_styles).toString(), 60 | style = document.createElement('style'); 61 | 62 | style.type = 'text/css'; 63 | style.appendChild(document.createTextNode(css)); 64 | 65 | document.head.appendChild(style); 66 | } 67 | 68 | And that's the crux of it! Now you can keep your component CSS and JS in the same file, easing development and maintainability. 69 | 70 | By wrapping your CSS code with annotations, you can strip them out when building for production, instead serving your CSS from a static file. 71 | 72 | To try the example, clone this repo, change to the example directory, run npm install then npm build. 73 | -------------------------------------------------------------------------------- /example/Button.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | var Button = React.createClass({ 4 | render: function() { 5 | return ( 6 |
My Button
7 | ) 8 | } 9 | }); 10 | 11 | require('../index')(` 12 | 13 | .btn 14 | display: inline-block 15 | background: blue 16 | padding: 5px 17 | 18 | &:hover 19 | background: red 20 | `) 21 | 22 | module.exports = Button; 23 | -------------------------------------------------------------------------------- /example/app.js: -------------------------------------------------------------------------------- 1 | var React = require('react'), 2 | Button = require('./Button'); 3 | 4 | React.render(