Adding React to a Middleman project with Webpack

Middleman is a static site generator written in Ruby. It's a great way to produce rich static content sites without the need for a server. Despite this, it doesn't come with internal support for modern JavaScript frameworks like React. Luckily, Middleman 4 ships with a feature called External Pipeline which allows wiring in your own external build tool like Gulp or Webpack.

Let's look at how to integrate Webpack with Middleman for the purpose of using React on our site.

Install development dependencies

First, let's install the dependencies we'll need to build our React code. Note that we're using the --save-dev flag to indicate to npm that we should append these libraries to the devDependencies section of our package.json.

$ npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react webpack uglifyjs-webpack-plugin
  • babel-core is the core Babel package for transpiling ES6 and JSX into browser-friendly ES5 JavaScript.
  • babel-loader is a Webpack loader which loads files from our path into Babel.
  • babel-preset-es2015 is a preset for Babel to transpile ES6 code.
  • babel-preset-react is a preset for Babel to transpile JSX code.
  • webpack is Webpack itself.
  • uglifyjs-webpack-plugin is a Webpack plugin to uglify and compress our code for production.

Install React

Next, install the React packages, this time using --save to indicate these are runtime dependencies:

$ npm install --save react react-dom

Set up your Babel configuration file

Babel has its own configuration file inside .babelrc. Create this file in the root of your Middleman project with the following contents:

{
  "presets": [
    "es2015", "react"
  ]
}

This file tells Babel to use the es2015 and react presets we installed in the first step.

Configure Webpack

Next we'll configure Webpack with a basic configuration file that supports both development and production environments. Place the following inside the file webpack.config.js in the root of your project:

// webpack.config.js
var webpack = require('webpack');

const isProduction = process.env.NODE_ENV === 'production';

const productionPlugins = [
  new webpack.DefinePlugin({
    'process.env.NODE_ENV': '"production"'
  }),
  isProduction ? new webpack.optimize.UglifyJsPlugin({
    compress: {
      warnings: false,
    },
  }) : null,
];

module.exports = {
  entry: './assets/javascripts/index.js',
  devtool: isProduction ? false : 'source-map',
  output: {
    library: 'MyApp',
    path: __dirname + '/tmp/dist',
    filename: 'bundle.js',
  },
  module: {
    loaders: [
      {
        test: /\.js$/,
        loader: 'babel-loader',
        exclude: /node_modules/
      }
    ]
  },
  plugins: isProduction ? productionPlugins : []
};

Notice that we use the isProduction flag to toggle the use of the UglifyJsPlugin as well as whether we use a devtool to provide us with source maps for debugging.

Configure Middleman to build Webpack

Now that we've configured Webpack, let's tell Middleman to execute it whenever it rebuilds. To do this, we'll activate the external pipeline plugin in our config.rb and point it at the Webpack executable:

# config.rb

activate :external_pipeline,
  name: :webpack,
  command: build? ?
  "NODE_ENV=production ./node_modules/webpack/bin/webpack.js --bail -p" :
  "./node_modules/webpack/bin/webpack.js --watch -d --progress --color",
  source: "tmp/dist",
  latency: 1

When we run middleman build, we run Webpack with NODE_ENV=production. This triggers our production build options in our Webpack configuration.

When using middleman server though, we tell Webpack to --watch for changes and automatically rebuild.

Middleman will look for the result of our built assets inside tmp/dist. Let's go ahead and create that directory now:

$ mkdir -p tmp/dist

Build an example React component

Now that we've got our tooling configured, let's create a simple React component to test that everything works. First, create a directory to store your Webpack JavaScript assets. I place these files in assets/javascripts since Sprockets looks inside source/javascripts, and I don't want Sprockets to attempt to build my React code.

$ mkdir -p assets/javascripts

Next, let's sketch out a React component. Note that from index.js I'm exporting a function renderHello which renders the HelloWorld component to a DOM element specified by ID. This allows us to call upon fragments of React code from within existing pages.

// assets/javascripts/index.js

import React from 'react';
import ReactDOM from 'react-dom';

const HelloReact = props => (
  <div>Hello, React!</div>
);

function renderHello(id) {
  const el = document.getElementById(selector);
  ReactDOM.render(<HelloReact />, el);
}

export default {
  renderHello
}

Mount the component

Finally, mount the component onto an existing DOM element on a page. First include the built bundle.js file. Then, call renderHello:

  <body>
    <div id="hello">
    </div>

    <%= javascript_include_tag  "bundle" %>
    <script type="text/javascript">
      MyApp.default.renderHello('hello');
    </script>
  </body>

Other resources

When you forget why

I don't ever want to forget why.

There are so many layers in what I do. From the sales process to the technical work to the administrative work.

It's easy to get caught in a cycle of continuing to do what you've always done without looking up from your work.

It's easy to forget that all this work is a means toward an end and not the end itself.

The end is my family.

The end is a cup of coffee enjoyed in tranquility on my porch.

The end is the cool breeze as I ride my bicycle along the river.

I want to serve others. But I'll serve them better if I serve myself.

Addicted to advice

Self-help books. Articles on Medium and Thought Catalog. Top ten lists. Seminars. Order my monthly coaching package.

And in rushes the anxiety. Are you doing everything you could be? Are you doing enough? Do you have what it takes? What will happen if you don't act now? Are you falling behind?

Are we addicted to advice? Are we listening too much too often to the babble of other people who tell us they know better how to be?

I challenge myself today to rely on instinct. To act on intuition. To beat my own drum and to listen to the song.

I'm Teejay, and I'm a recovering advice addict.

Integrity

Integrity is a virtue.

There are a million ways to make your website convert better. But some of them abuse the visitor and dangle carrots that don't help them.

I can't remember the last time I saw an on-exit popup that made me smile.

It's easy to make a living selling the idea of becoming rich or famous and then not deliver.

It's harder to make a living encouraging someone to do their best and genuinely help them get as far as they can.

I don't want to sell shovels to miners. My vow to my clients is simple: I will always act in your best interest.

Fighting the urge to remain in motion

Do you ever find yourself involuntarily trending toward making life more difficult than it need be? To crave change and excitement—the very same that you were trying to eradicate through the simplicity and calm you now inhabit?

I've felt that way recently. A drive to shake things up for the sake of shaking them up. Funny though; where I am now was a dream only a year ago.

It pays to reflect on our journey occasionally to recognize our progress. But fighting the urge to remain in motion is futile until we reach the grave.

Remote for life

The last time I went to an office was in 2007.

I remember never quite feeling at ease. Wanting to work but feeling like there was a pressure to stay for the full eight hours. Not feeling like I could go take a break to clear my mind. Subordination.

Ten years later and I've invested in working from home.

I've built myself a lovely minimalist workstation where I'm able to be productive without distractions. I work in my sweatpants and make my own lunches. I'm simultaneously productive and happy and free. This is the lifestyle that works for me.

I know I might be missing out on career opportunities because of my stubbornness to work from home, but in my view they're not worth the commutes and the feeling of entrapment.

There was an article in the New York Times yesterday about people with commutes more than 2 hours. If you work an 8-hour day and commute 4 hours per day to get there and back, that's a 33% pay cut.

My trip to Portland reminded me how city commuting can be stressful. The busyness and the sense we all have to be somewhere fast. From my perspective, we ought to spend our time figuring out how not to do that anymore.

That's why I've built my life around working remotely. And while I might turn down opportunities to grow, I know I'm in control of my own time.

The Portland I used to know

I went to Portland this past weekend to attend Edward Tufte's excellent Presenting Data and Information course.

Having lived there for the better part of a decade, I've always thought of Portland as my adulthood home. A place to which I'd return someday. A place bookmarked in time.

But now I'm not so sure. The experience I had in Portland this time left my befuddled: Had Portland changed so dramatically in the three years I'd been gone, or did my own values change?

My friends there say it's probably a bit of both. I remember a Portland where ordinary people could afford to open small businesses. Now it seems as though all of those lovely local businesses are closing. I'm not opposed to change and certainly don't think preservation legislation is the answer, but it's a difficult and depressing pill to swallow.

And can we talk about the cultural shift? I don't mean to stereotype, but I'm about to. When did Portland go from a place where the punks and weirdos thrive to a place where it seems as though people go to great lengths to manicure their appearance to the point of absurdity? Was Portland always the epicenter of douchey-cool and I've just grown out of it? Or has it reached its tipping point?

I still love the Portland cityscape and don't bemoan out-of-towners who dream of moving there. One of the unique draws of city life is being surrounded by people different from you. But Portland's recent homogenization represents a shift away from that diversity. I'm not talking about racial diversity or even ethnic diversity. I mean diversity of ideas. A place where both artists and businesspeople can thrive. That's the Portland I left. And now I'm not so sure I'll go back.

Buffers

Life is peaceful when there are buffers.

The time between the present moment and your next obligation is a buffer. The money in your bank account that protects you from insolvency is a buffer. Food in the pantry. The space between your neighbor's house and your own.

As I've grown older, I've noticed I want wider buffers. I'm less willing to allow them to shrink to their size ten years ago. Busyness. Brokeness. Empty pantries and tiny apartments.

Part of me misses that wild abandon. But when I sleep at night knowing there's a cushion between me and the world, I smile.

How to name and organize your React components

When I first began building complex React applications, I struggled to determine the best way to name and organize my components within my codebase. Having come from the Rails school of convention over configuration, I was perplexed to find that most modern JavaScript apps rely heavily on custom configuration and don't adhere to any sort of community-driven conventional norms. That's changing slowly with the advent of toolkits like create-react-app, but even its conventions go right out the window as soon as you npm eject.

After a couple years of learning and mistakes, there are a few guidelines I use when organizing my React components so my code is more readable, understandable, and succinct.

Compose your components into smaller subcomponents

While it's tempting to just keep adding bits and pieces to your component's render method, this can grow to the point where it becomes difficult for new eyes to discern your intentions. Imagine a render method like this:

render() {
  return (
    <div className="todo-list">
      <div className="todo-list__items">
        {this.props.items.map(item => (
          <div className="todo-item">
            {item.title}
          </div>
        ))}
      </div>
    </div>
  );
}

Instead of rendering todo items within the root component's render method, create a new component:

const TodoItem = props => (
  <div className="todo-item">
    {props.item.title}
  </div>
);

Then, call it in our root component's render method:

render() {
  return (
    <div className="todo-list">
      <div className="todo-list__items">
        {this.props.items.map(item => (
          <TodoItem item={item} />
        ))}
      </div>
    </div>
  );
}

It's a subtle change, but making these sorts of changes proactively can keep your components readable. And becuase React only re-renders those components that change, extracting smaller components can improve your application's performance.

Extract iterators into class methods or new components

Extracting a subcomponent is a good first step. But we can go one step further by extracting the map iterator call into its own method in the root component. This improves the readability of the render method:

render() {
  return (
    <div className="todo-list">
      <div className="todo-list__items">
        {this.renderItems()}
      </div>
    </div>
  );
}

renderItems() {
  return this.props.items.map(item => (
    <TodoItem item={item} />
  ));
}

Now when we scan the render method, we see a more succinct summary of its contents!

Only use ES6-style class components where state is needed

Notice in my previous example that I've opted to use const to define the TodoItem component. This is because, in its current incarnation, the TodoItem component is stateless.

In React, stateless components are merely functions that return a React-wrapped DOM element. Unlike using the ES6 class keyword or the now-antiquated React.createClass method, stateless components cannot hold their own component state in a this.state object.

The reason this syntax is favorable is because it encourages authoring code in a functional style. Let's expand our TodoItem component to include a "Delete" button:

const TodoItem = props => (
  <div className="todo-item">
    <div className="todo-item__title">
      {props.item.title}
    </div>

    <button onClick={props.onDelete}>
      Delete
    </button>
  </div>
);

Here we've enclosed the title in a new container div, and then added a <button> tag with an onClick handler set to a hypothetical onDelete thunk handler that we would pass through from the parent component.

Just like in our iterator extraction example above, there's opportunity to make this component more readable. However, because this component is stateless we can use a more functional style:

const Title = props => (
  <div className="todo-item__title">
    {props.value}
  </div>
);

const DeleteButton = props => (
  <button onClick={props.onDelete}>
    Delete
  </button>
);

const TodoItem = props => (
  <div className="todo-item">
    <Title value={props.item.title} /> <DeleteButton
    <DeleteButton onClick={props.onDelete} />
  </div>
);

Our goal in doing these extractions is to reduce fatigue when scanning your components' code. New developers who visit this code for the first time will be greeted with components whose markup is only a few lines, making it much easier to parse and understand than if they were tens or hundreds of lines.

Keeping this habit early on will mean your codebase can grow and remain understandable to newcomers.

Organize composed components into subdirectories

So far, we've built the following components:

  • TodoList
  • TodoItem
  • Title
  • DeleteButton

As our codebase grows, the way we physically organize our code on disk is going to become more critical. I've experimented with a couple methodologies.

The first is to create a directory called components, dump all your components in there, and call it a day. This is fine for projects with fewer than 20 or so components, but it becomes cumbersome as the number of components grows.

Instead, I've settled on creating subdirectories for certain root-level components. In our example, we could envision the following directory structure:

  components/
    TodoList/
      index.js
      Item/
        index.js
        Title.js
        DeleteButton.js

Organizing our components in this way has the following benefits:

  • Components only ever reference other components within their own subdirectory.
  • We can name components based on their context. For instance, instead of naming our item component TodoItem, we can call it Item. This reduces unnecessary verbosity.
  • Our components become portable. By encapsulating their hierarchy within a single directory, we can reuse the component in other codebases easily.

That feeling when you want to give up

Marketing yourself sure is an anxious chore. I'm plenty qualified for all sorts of full-time jobs, but I'm resolute against taking one since I know I work best when I'm free.

What does freedom mean to me?

Freedom means the ability to wake up when my body tells me instead of when an alarm sounds its siren. It means I can take some time between clients to ride my bicycle around town casually without worrying that I need to return to the office. It means I'm not burdened by day-to-day inter-office politics. That I can provide immense value without being physically present.

But it's tough out there. Not in the economic sense; there's probably plenty of work to be done. But marketing yourself as a consultant is no easy task. Most consultants probably wouldn't admit such a thing on their website for fear of being perceived as a failure or a fake.

I'm not afraid of that because I know my value, but I am afraid of failure. I'm afraid I'll soon be applying for jobs and working 40 hours per week and giving up on this whole consulting thing for good. Which is humorous in its own way given the fact I'm nowhere near failing. But that's how fear works, isn't it?