Popup With ReactJS For Jekyll Blog

Once upon a time (yesterday) I decided that my blog lacks a good old pop up. You know, the one that unannoyingly asks to join the mailing list. So if you have Jekyll blog and you want to know how to create a pop up using ReactJS –this post is totally relevant.

First let’s get all pragmatic and see an example of what we’ll get.

In this article I’m assuming that you are using Github pages to host your Jekyll blog.

Let’s Get Our Hands Dirty

To be able to compile .jsx code – first we need to setup webpack.

Start by creating package.json in your root directory.

➜ npm init

After answering all required question and cleaning out unnecessary stuff you should end up with something like this:

{
  "name": "satansdeer.github.com",
  "version": "1.0.0",
  "description": "Maksim Ivanov jekyll blog",
  "author": "Maksim Ivanov",
  "license": "ISC",
  "devDependencies": {
  }
}

Now let’s install required dependencies:

➜ npm install webpack babel-core babel-loader babel-preset-es2015 babel-preset-react react react-dom babel-plugin-transform-class-properties --save-dev

Create the .babelrc with presets configuration:

➜ echo '{ "presets": ["react", "es2015"] }' > .babelrc

Configure webpack, put this in your webpack.config.js:

const path = require('path');

module.exports = {
  entry: "./front/entry.js",
  output: {
    path: path.join(__dirname, "/assets/javascripts"),
    filename: "bundle.js"
  },
  module: {
  loaders: [
    {
      test: /\.jsx?$/,
      exclude: /(node_modules)/,
      loader: "babel-loader",
      query: {
        plugins: ['transform-class-properties']
      }
    }
    ]
  }
};

Test Run

Make front folder:

mkdir front

And create file entry.js there with some console output for testing.

echo 'console.log("It works!")' > front/entry.js

Run webpack, it should build your bundle.js inside /assets/javascripts folder.

➜ webpack

Now put add this line in your layout. I don’t know maybe in your footer or just in the bottom of your _layouts/default.html:

<script type="text/javascript" src="/assets/javascripts/bundle.js" charset="utf-8"></script>

Open your site. You should see It works! in Javascript console. If not – double check webpack config and make sure that bundle.js was built.

Add React

Create components folder:

mkdir front/components

And add App.js there with following contents:

import React, { Component } from 'react';
import Popup from './Popup';
import SubscriptionForm from './SubscriptionForm';

class App extends Component {
  constructor(props) {
    super(props);

    this.state = { isOpen: false };
  }

  openPopup = () => {
    this.setState({
      isOpen: true
    });
  }

  closePopup = () => {
    this.setState({
      isOpen: false
    });
  }

  render() {
    return (
      <div className="App">
        <button onClick={this.openPopup}>
          Click Me!
        </button>

        <Popup show={this.state.isOpen}
          onClose={this.closePopup}>
          <SubscriptionForm></SubscriptionForm>
        </Popup>
      </div>
    );
  }
}

export default App;

Here each time we call openPopup or closePopup we trigger setState function that causes re-render with updated isOpen value.

Then we pass isOpen value to Popup component. Create Popup.js with the following code:

import React from 'react';

class Popup extends React.Component {
  render() {
    if(!this.props.show) {
      return null;
    }

    return (
      <div className="popup-backdrop">
        <div className="popup">
          <button className="popup-close" onClick={this.props.onClose}></button>
          {this.props.children}
        </div>
      </div>
    );
  }
}

export default Popup;

It returns null and renders nothing if we pass false, or renders our pop up layout if we pass true.

We’ve also passed closePopup as value of onClosed prop. And now we trigger it as onClick callback of close button.

The {this.props.children} part allowed us to pass the nested component SubscriptionForm that basically contains opt-in form provided by Mailchimp.

I just changed inputs to use closed tag, and changed some attributes to their React versions. Like class/className, for/htmlFor.

Here is my SubscriptionForm:

import React from 'react';
import PropTypes from 'prop-types';

class SubscriptionForm extends React.Component {
  render() {
    return (
      <div id="mc_embed_signup">
        <form action="https://maksimivanov.us12.list-manage.com/subscribe/post?u=fdcb5a4b4a6cbb9721227a48f&amp;id=fa1a88a0d0" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" className="validate" target="_blank" noValidate>
          <div id="mc_embed_signup_scroll">
            <h2>Subscribe to my mailing list</h2>
            <div className="mc-field-group">
              <label htmlFor="mce-NAME">Name:
                <input type="text" name="NAME" className="required" id="mce-NAME"/>
              </label>
            </div>
            <div className="mc-field-group">
              <label htmlFor="mce-EMAIL">Email:
                <input type="email" name="EMAIL" className="required email" id="mce-EMAIL"/>
              </label>
            </div>
            <div id="mce-responses" className="clear">
              <div className="response" id="mce-error-response" style={{display: 'none'}}></div>
              <div className="response" id="mce-success-response" style={{display: 'none'}}></div>
            </div>
            <div style={{position: 'absolute', left: '-5000px'}} aria-hidden="true"><input type="text" name="b_fdcb5a4b4a6cbb9721227a48f_fa1a88a0d0" tabIndex="-1" value=""/></div>
            <div className="clear"><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" className="button"/></div>
          </div>
        </form>
      </div>
    );
  }
}

export default SubscriptionForm;

It is temporal solution, later I plan to switch to mail-for-good that uses AWS to send bulk emails and rewrite that subscription form as well.

Summary

This is it. We’ve used webpack to build and bundle our Javascript. We’ve added a simple pop up component and an opt-in form.

In next article we’ll add tests and learn about some tools that we have for testing React applications.