How To Style a React Application with Blueprint.js

Wed Jul 19, 2017 - 1000 Words

Having a functional application is one thing, but at a certain point, the application’s UI needs some attention. In this tutorial, we’ll be pulling in Blueprint.js to help us clean up our UI.

Goals

  • Install blueprint.js into our existing React application.
  • Utilize the blueprint’s UI toolkit to improve the look of the application.

Since React applications are made of components from top to bottom, we have the ability to pull in components that other people have already created. We’ve already done this by pulling in [React Router][2], but those components are functional rather than being visual. Being able to leverage Open Source components can help move a project along (especially if you’re not great with CSS). Blueprint.js is a mix of both CSS components and JavaScript components, and we’ll use both.

Installing Blueprint.js

We can’t utilize the Blueprint components until we install the package and import the components. Let’s install the package from NPM (using yarn):

$ yarn add @blueprint/core

You should see a message someone in the yarn output about having ‘unmet peer dependencies’ because we haven’t installed react-addons-css-transition-group, so let’s also install that.

$ yarn add react-addons-css-transition-group

With those two packages successfully installed we’re ready to use Blueprint, but we also need to import the styles into our index.js.

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import registerServiceWorker from './registerServiceWorker';

import 'normalize.css/normalize.css';
import '@blueprintjs/core/dist/blueprint.css';

ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();

Adding Navigation

Not all of the components in Blueprint are JavaScript components, some of them are plain old HTML and CSS. The navigation component is one of those, and we’ll use it to replace our Header component’s content. Here’s the final Header component.

src/components/Header.js

import React, { Component } from 'react';
import { Link } from 'react-router-dom';

class Header extends Component {
  render() {
    return (
      <nav className="pt-navbar">
        <div className="pt-navbar-group pt-align-left">
          <div className="pt-navbar-heading">Chord Creator</div>
          <input className="pt-input" placeholder="Search songs..." type="text" />
        </div>
        <div className="pt-navbar-group pt-align-right">
          <Link className="pt-button pt-minimal pt-icon-music" to="/songs">Songs</Link>
          <span className="pt-navbar-divider"></span>
          <button className="pt-button pt-minimal pt-icon-user"></button>
          <button className="pt-button pt-minimal pt-icon-cog"></button>
        </div>
      </nav>
    );
  }
}

export default Header;

Styling the Song List

Now that we’ve added a little bit of navigation we can click something from anywhere on the site and get back to the song list. Let’s style the song list to make it a little more presentable. The first thing that we’re going to do is extract the listing of songs into a separate component.

src/components/SongList.js

import React, { Component } from 'react'
import { Link } from 'react-router-dom'

const songListStyles = {
  display: "flex",
  flexDirection: "row",
  flexWrap: "wrap",
  justifyContent: "center",
}

const songCardStyles = {
  maxWidth: "30%",
  minWidth: "150px",
  flex: "1",
  margin: "5px",
}

class SongList extends Component {
  render() {
    const { songs } = this.props
    const songIds = Object.keys(songs)

    return (
      <div>
        <h1 style={{marginBottom: "0.5em"}}>Songs</h1>

        <div style={songListStyles}>
          {songIds.map((id) => {
            const song = songs[id]
            return (
              <div key={id} style={songCardStyles} className="pt-card pt-elevation-0 pt-interactive">
                <h5><Link to={`/songs/${song.id}`}>{song.title}</Link></h5>
              </div>
            )
          })}
        </div>
      </div>
    )
  }
}

export default SongList

Style wise we did a few different things here. We’ve created some JavaScript objects for songListStyles and songCardStyles that we then use to set inline styles on the divs containing our songs. These will be written out as inline styles when the markup is rendered, but we don’t need to interact with them in code like they are strings. For the h1 and the wrapper div we set some inline styles directly. Lastly, we’re using the pt-card component from Blueprint and rending the song’s title. Next, we need to utilize this new SongList component in App:

src/components/App.js

  // Remainder of file omitted
  render() {
    return (
      <div style={{maxWidth: "1160px", margin: "0 auto"}}>
        <BrowserRouter>
          <div>
            <Header />
            <div className="main-content" style={{padding: "1em"}}>
              <div className="workspace">
                <Route exact path="/songs" render={(props) => {
                    return <SongList songs={this.state.songs} />
                }} />
              <Route path="/songs/:songId" render={(props) => {
                const song = this.state.songs[props.match.params.songId];
                return (
                  song
                  ? <ChordEditor song={song} updateSong={this.updateSong} />
                  : <h1>Song not found</h1>
                )
              }} />
            </div>
          </div>
          <Footer />
        </div>
      </BrowserRouter>
    </div>
    );
  }
  // omitted

We’ve left most of the file out because the only changes we made outside of render were that we imported the SongList component and remove the import of Link since it’s no longer used in this file. We substitute the map logic that we had before with SongList and added a few inline styles to the wrapper div to help the application breath a little.

Cleaning up the ChordEditor UI

The main portion of our application is the ChordEditor, and the bulk of the changes remaining in this application revolve around it, but in this tutorial, we’re only going to clean up the area around the editor.

src/components/ChordEditor.js

import React, { Component } from 'react';
import { Breadcrumb } from '@blueprintjs/core';
import ChordSheetJS from 'chordsheetjs';

class ChordEditor extends Component {
  // Other functions omitted, unchanged

  render() {
    return (
      <div>
        <ul className="pt-breadcrumbs">
          <li><Breadcrumb href="/songs" text="Songs" /></li>
          <li><Breadcrumb href="#" text={this.props.song.title} /></li>
        </ul>
        <h2 style={{margin: "0.5em 0"}}>{this.props.song.title}</h2>
        <div className="chord-editor">
          <div className="panel">
            <h3>Input</h3>
            <textarea
              style={{width: "100%", height: "100%"}}
              onChange={this.handleChange}
              value={this.props.song.chordpro}/>
          </div>
          <div className="panel">
            <h3>Output</h3>
            <div
              style={{width: "100%", height: "100%", fontFamily: "monospace"}}
              className="chord-output"
              dangerouslySetInnerHTML={this.getChordMarkup()}/>
          </div>
        </div>
      </div>
    );
  }
}

export default ChordEditor;

The biggest change here is that we’ve added the song title to the page and we’ve utilized another component of Blueprint.js to build breadcrumbs. The Breadcrumb component itself is pretty simple and outputs an anchor tag to go into our breadcrumb ul > li that we’ve decorated with the Blueprint classes. These changes aren’t huge, but they’ll make it easier to know which song is being worked on at the moment.

Recap

We took a look at how we can integrate external styles into our application by pulling in Blueprint.js and did a little bit of work to clean up the UI. It is much easier to work on an application once it has a little bit of aesthetic appeal and personality, so these changes will make working with this application better as we move forward.