Creating reusable HTML styled components with react-jss

Unni Krishnan D
5 min readJul 21, 2018

One of the biggest challenges developers face while developing a web application is how to efficiently manage CSS.Traditional CSS can generate a lot of dead code in our application and thereby impacting the performance as well as size of our bundle.

Traditional CSS is all about static files which contains key(CSS property) and values.In normal scenarios we will be loading a lot of unwanted CSS in our application.Imagine a scenario where we can write CSS in JavaScript as objects or components and the unlimited possibilities to harness the power of JavaScript.

Lets Dig in :-)

Prerequisites

A basic understanding of ReactJS,states,props ,higher order components and most importantly manipulation of JavaScript objects.

Installation

we will be using Facebook’s create-react-app in this tutorial and yarn as package manager along with the package react-jss

Bootstrapping

yarn create react-app demoapp_reactjss

Installing react-jss

cd demoapp_reactjss
yarn add react-jss

Once bootstrapping and installation is done we can create our project structure.I'm creating a contact-us page with theming support.Create files as per the screenshot.We will go through each files in the next section.

Files

App.js :: Our Main component file where we will be loading all our sub components.
Reusables/inputs.js ::Our main goal was the make it reusable as much as possible.For a contact-us page we require components like Inputs,Buttons ,labels etc.

import React, { Component } from ‘react’;
class Input extends Component {
render() {
return (
<div className=”Input”>
<input type=”text” placeholder={this.props.placeholder} className={this.props.classes.myInput} />
</div>
);
}
}
class TextArea extends Component {
render() {
return (
<div className=”TextArea”>
<textarea placeholder={this.props.placeholder} className={this.props.classes.myTextarea}></textarea>
</div>
);
}
}
class Layout extends Component {
render() {
return (
<div className={this.props.classes.layout}>
{this.props.children}
</div>
)
}
}
class Button extends Component {
render() {
return (
<div className={this.props.classes.btnContainer} onClick={this.props.clickEvent}>
<button className={this.props.classes.btn}>
{this.props.children}
</button>
</div>
);
}
}
class Label extends Component {
render() {
return (
<div className={this.props.classes.parentDiv}>
<label className={this.props.classes.lbl}>{this.props.children}</label>
</div>
)
}
}
const Reusables = {
Button :Button ,
TextArea : TextArea ,
Input : Input,
Layout : Layout,
Label : Label
};
export default Reusables;

So we have created 5 reusable components(Button,Label,Input,TextArea,Layout). We already have various props injected in these components.

colors.js

We are storing colors as an object.This is not CSS.

const colors = {
default : {
buttoncolor : "#581845",
bordercolor :"#e2aea3",
bgcolor:"#DAF7A6"
},
dark : {
buttoncolor : "#001243",
bordercolor :"#581845",
bgcolor:"#C70039"
}
};
export default colors;

default.js

Here also we are using one JavaScript object.We have different sections like button,input ,layout etc.

import colors from './colors.js';
const defaulttheme = {
button : {
buttonDefaultColor : colors.dark.buttoncolor,
bordercolor : colors.dark.bordercolor,
fontWeight : "bold",
borderWidth :"1px",
borderStyle : "solid",
width : "120px",
fontFamily :"Calibri"
},
input : {
buttonDefaultColor : colors.dark.buttoncolor,
bordercolor : colors.dark.bordercolor,
fontWeight : "bold",
borderWidth :"1px",
borderStyle : "solid",
width : "120px"
},
textarea : {
buttonDefaultColor : colors.dark.buttoncolor,
bordercolor : colors.dark.bordercolor,
fontWeight : "bold",
borderWidth :"1px",
borderStyle : "solid",
width : "150px"
},
label : {
buttonDefaultColor : colors.dark.buttoncolor,
bordercolor : colors.dark.bordercolor,
fontWeight : "bold",
width : "120px",
minWidth:"120px",
fontFamily :"Calibri"
},
layout : {
bgcolor : colors.dark.bgcolor
}
};
export default defaulttheme;

dark.js

The same file as default.js.But with different values

import colors from './colors.js';
const darktheme= {
button : {
buttonDefaultColor : colors.dark.buttoncolor,
bordercolor : colors.dark.bordercolor,
fontWeight : "bold",
borderWidth :"1px",
borderStyle : "solid",
width : "120px",
fontFamily :"Calibri"
},
input : {
buttonDefaultColor : colors.dark.buttoncolor,
bordercolor : colors.dark.bordercolor,
fontWeight : "bold",
borderWidth :"1px",
borderStyle : "solid",
width : "120px"
},
textarea : {
buttonDefaultColor : colors.dark.buttoncolor,
bordercolor : colors.dark.bordercolor,
fontWeight : "bold",
borderWidth :"1px",
borderStyle : "solid",
width : "150px"
},
label : {
buttonDefaultColor : colors.dark.buttoncolor,
bordercolor : colors.dark.bordercolor,
fontWeight : "bold",
width : "120px",
minWidth:"120px",
fontFamily :"Calibri"
},
layout : {
bgcolor : colors.dark.bgcolor
}
};
export default darktheme;

Next we need to create the JSS components.For better modularity i have created one JSS file for each components.A JSS component is a ReactJS component where we can pass values as props from the parent component as well as values from the theme file.

button.js
Here btn is the className we are using in our reusable component.The values are from theme(theme.button.FontFamily) as well as props(props.borderRadius).

const Buttonstyles = theme => ({btn: props => ({
color: theme.button.buttonDefaultColor,
border : theme.button.borderWidth+" "+ theme.button.borderStyle+" "+theme.button.bordercolor,
width : theme.button.width,
fontWeight : theme.button.fontWeight,
borderRadius : props.borderRadius +"px",
fontFamily:theme.button.fontFamily
})
});
export default Buttonstyles;

Other JSS files are attached at the end of the article.

Now Integrating everything in our App.js

import React, { Component } from 'react';
import injectSheet, {ThemeProvider} from 'react-jss';
//reusable components
import Reusables from './reusables/inputs';
//styles
import Buttonstyles from './styles/button.js';
import InputStyles from './styles/input.js';
import TextAreaStyles from './styles/textarea.js';
import LayoutStyles from './styles/layout.js';
import LabelStyles from './styles/label.js';
//themes
import defaulttheme from './styles/themes/default.js';
import darktheme from './styles/themes/dark.js';
class App extends Component {
constructor() {
super();
//set up a theme state ,defaulttheme
this.state = {
theme : defaulttheme,
statusmsg : ""
}
}
changeTheme(t) {
//this function will toggle the theme
this.setState({
theme : (t == "dark") ? darktheme : defaulttheme
})
}
clickContact() {
//just a function for the click
this.setState ({
statusmsg :"Hello thanks for contacting us !,We will get back to you"
})
console.log("Click");
}
render() {
//each of these inject methods creates reusable elements from the Reusables and injects with the style from theme
const Input = injectSheet(InputStyles)(Reusables.Input);
const StyledButton = injectSheet(Buttonstyles)(Reusables.Button);
const TextArea = injectSheet(TextAreaStyles)(Reusables.TextArea);
const LayoutComponent = injectSheet(LayoutStyles)(Reusables.Layout);
const Label = injectSheet(LabelStyles)(Reusables.Label)
return (
<div className="App">
Change theme <button onClick={this.changeTheme.bind(this,'dark')}>Dark</button>
<button onClick={this.changeTheme.bind(this,'light')}>Light</button>
<ThemeProvider theme={this.state.theme}>
<div>
<LayoutComponent direction="column" justify="flex-start" ><LayoutComponent direction="row" justify="flex-start">
<Label>Name</Label>
<Input borderRadius="5" />
</LayoutComponent>
<LayoutComponent direction="row" justify="flex-start">
<Label>Email</Label>
<Input borderRadius="5" />
</LayoutComponent>
<LayoutComponent direction="row" justify="flex-start">
<Label>Address</Label>
<TextArea borderRadius="5" />
</LayoutComponent>
</LayoutComponent><StyledButton borderRadius="5" clickEvent={this.clickContact.bind(this)}>Contact Us</StyledButton>
<p>{this.state.statusmsg}</p>
</div>
</ThemeProvider>
</div>
);
}
}
export default App;

Explanation of App.js (How it works)

const Input = injectSheet(InputStyles)(Reusables.Input);

This creates a Input Component from the reusables/inputs.js

<ThemeProvider theme={this.state.theme}>

This creates a container for theming with theme set to state.theme

<LayoutComponent direction="row" justify="flex-start">
<Label>Name</Label>
<Input borderRadius="5" />
</LayoutComponent>

Here the props direction,justify are accessible in the corresponding JSS file (style/layout.js). The Label and Input are rendered inside the Layout Component from reusables/inputs.js.

class Layout extends Component {
render() {
return (
<div className={this.props.classes.layout}>
{this.props.children}//here we get the Label and Input
</div>
)
}
}

And finally we have a state toggler for theme

changeTheme(t)  {
//this function will toggle the theme
this.setState({
theme : (t == "dark") ? darktheme : defaulttheme
})
}

Dark theme

Default theme (Light)

Conclusion

As you can see we can only include the JSS files needed for a component there by reducing the size of bundle and dead CSS code.DOM Manipulations w.r to CSS(Like Jquery ) also can be achieved easily.

Source files

Download

Extract

run
yarn install

yarn start

navigate to localhost:3000/

--

--