diff --git a/.babelrc b/.babelrc
new file mode 100644
index 000000000..43777946d
--- /dev/null
+++ b/.babelrc
@@ -0,0 +1,3 @@
+{
+    "presets": ["es2015", "react"],
+}
diff --git a/.eslintrc b/.eslintrc
deleted file mode 100644
index 9cadb556a..000000000
--- a/.eslintrc
+++ /dev/null
@@ -1,29 +0,0 @@
-{
-    "parser": "babel-eslint",
-    "rules": {
-        "curly": [2, "multi-line"],
-        "eol-last": [2],
-        "indent": [2, 4],
-        "linebreak-style": [2, "unix"],
-        "max-len": [2, 120, 4, {"ignoreUrls": true}],
-        "no-trailing-spaces": [2, { "skipBlankLines": true }],
-        "no-unused-vars": [2, {"args": "after-used", "varsIgnorePattern": "^_"}],
-        "quotes": [2, "single"],
-        "semi": [2, "always"],
-        "space-before-function-paren": [2, "always"],
-        "strict": [2, "never"]
-    },
-    "env": {
-        "browser": true,
-        "es6": true,
-        "node": true
-    },
-    "globals": {
-        "formatMessage": true
-    },
-    "plugins": [
-        "react",
-        "json"
-    ],
-    "extends": "eslint:recommended"
-}
diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 000000000..36ff570d9
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,3 @@
+module.exports = {
+    extends: ['scratch', 'scratch/node']
+};
diff --git a/Makefile b/Makefile
index 65a57a3ac..abe7f3a72 100644
--- a/Makefile
+++ b/Makefile
@@ -55,7 +55,7 @@ test:
 	@make tap
 
 lint:
-	$(ESLINT) . --ext .js,.jsx,.json
+	$(ESLINT) . --ext .js,.jsx
 	$(SASSLINT) ./src/*.scss
 	$(SASSLINT) ./src/**/*.scss
 
diff --git a/bin/configure-fastly.js b/bin/configure-fastly.js
index edb7196ad..a935b906e 100644
--- a/bin/configure-fastly.js
+++ b/bin/configure-fastly.js
@@ -3,7 +3,7 @@ var defaults = require('lodash.defaults');
 var fastlyConfig = require('./lib/fastly-config-methods');
 const languages = require('../languages.json');
 
-var route_json = require('../src/routes.json');
+var routeJson = require('../src/routes.json');
 
 const FASTLY_SERVICE_ID = process.env.FASTLY_SERVICE_ID || '';
 const S3_BUCKET_NAME = process.env.S3_BUCKET_NAME || '';
@@ -15,10 +15,10 @@ var extraAppRoutes = [
     // TODO: Should this be added for every route?
     '/\\?',
     // View html
-    '/[^\/]*\.html$'
+    '/[^/]*.html$'
 ];
 
-var routes = route_json.map(function (route) {
+var routes = routeJson.map(function (route) {
     return defaults({}, {pattern: fastlyConfig.expressPatternToRegex(route.pattern)}, route);
 });
 
@@ -28,9 +28,9 @@ async.auto({
             if (err) return cb(err);
             // Validate latest version before continuing
             if (response.active || response.locked) {
-                fastly.cloneVersion(response.number, function (err, response) {
-                    if (err) return cb('Failed to clone latest version: ' + err);
-                    cb(null, response.number);
+                fastly.cloneVersion(response.number, function (e, resp) {
+                    if (e) return cb('Failed to clone latest version: ' + e);
+                    cb(null, resp.number);
                 });
             } else {
                 cb(null, response.number);
@@ -46,11 +46,11 @@ async.auto({
         var recvCondition = '' +
             'if (' + notPassStatement + ') {\n' +
             '    set req.backend = F_s3;\n' +
-            '    set req.http.host = \"' + S3_BUCKET_NAME + '\";\n' +
+            '    set req.http.host = "' + S3_BUCKET_NAME + '";\n' +
             '} else {\n' +
             '    if (!req.http.Fastly-FF) {\n' +
             '        if (req.http.X-Forwarded-For) {\n' +
-            '            set req.http.Fastly-Temp-XFF = req.http.X-Forwarded-For \", \" client.ip;\n' +
+            '            set req.http.Fastly-Temp-XFF = req.http.X-Forwarded-For ", " client.ip;\n' +
             '        } else {\n' +
             '            set req.http.Fastly-Temp-XFF = client.ip;\n' +
             '        }\n' +
@@ -171,20 +171,19 @@ async.auto({
             if (err) return cb(err);
             cb(null, headers);
         });
-    }]},
-    function (err, results) {
-        if (err) throw new Error(err);
-        if (process.env.FASTLY_ACTIVATE_CHANGES) {
-            fastly.activateVersion(results.version, function (err, response) {
-                if (err) throw new Error(err);
-                process.stdout.write('Successfully configured and activated version ' + response.number + '\n');
-                if (process.env.FASTLY_PURGE_ALL) {
-                    fastly.purgeAll(FASTLY_SERVICE_ID, function (err) {
-                        if (err) throw new Error(err);
-                        process.stdout.write('Purged all.\n');
-                    });
-                }
-            });
-        }
+    }]
+}, function (err, results) {
+    if (err) throw new Error(err);
+    if (process.env.FASTLY_ACTIVATE_CHANGES) {
+        fastly.activateVersion(results.version, function (e, resp) {
+            if (err) throw new Error(e);
+            process.stdout.write('Successfully configured and activated version ' + resp.number + '\n');
+            if (process.env.FASTLY_PURGE_ALL) {
+                fastly.purgeAll(FASTLY_SERVICE_ID, function (error) {
+                    if (error) throw new Error(error);
+                    process.stdout.write('Purged all.\n');
+                });
+            }
+        });
     }
-);
+});
diff --git a/bin/lib/fastly-config-methods.js b/bin/lib/fastly-config-methods.js
index 750b1893b..d6a8c973d 100644
--- a/bin/lib/fastly-config-methods.js
+++ b/bin/lib/fastly-config-methods.js
@@ -24,9 +24,9 @@ var FastlyConfigMethods = {
      */
     getViewPaths: function (routes) {
         return routes.reduce(function (paths, route) {
-            var path = route.routeAlias || route.pattern;
-            if (paths.indexOf(path) === -1) {
-                paths.push(path);
+            var p = route.routeAlias || route.pattern;
+            if (paths.indexOf(p) === -1) {
+                paths.push(p);
             }
             return paths;
         }, []);
@@ -39,7 +39,7 @@ var FastlyConfigMethods = {
      * 2. /path/:arg([regex]) – :arg is removed, leaving just /path/([regex])
      */
     expressPatternToRegex: function (pattern) {
-        pattern = pattern.replace(/(:\w+)(\([^\)]+\))/gi, '$2');
+        pattern = pattern.replace(/(:\w+)(\([^)]+\))/gi, '$2');
         return pattern.replace(/(:\w+)/gi, '.+?');
     },
 
@@ -84,7 +84,7 @@ var FastlyConfigMethods = {
         return 'redirects/' + route.pattern;
     },
 
-    /**
+    /*
      * Returns custom vcl configuration as a string that sets the varnish
      * Time to Live (TTL) for responses that come from s3.
      *
diff --git a/bin/lib/fastly-extended.js b/bin/lib/fastly-extended.js
index 3ec9fd7e3..0e4467221 100644
--- a/bin/lib/fastly-extended.js
+++ b/bin/lib/fastly-extended.js
@@ -19,8 +19,8 @@ module.exports = function (apiKey, serviceId) {
      *
      * @return {string}
      */
-    fastly.getFastlyAPIPrefix = function (serviceId, version) {
-        return '/service/' + encodeURIComponent(serviceId) + '/version/' + version;
+    fastly.getFastlyAPIPrefix = function (servId, version) {
+        return '/service/' + encodeURIComponent(servId) + '/version/' + version;
     };
 
     /*
@@ -32,15 +32,15 @@ module.exports = function (apiKey, serviceId) {
         if (!this.serviceId) {
             return cb('Failed to get latest version. No serviceId configured');
         }
-        var url = '/service/'+ encodeURIComponent(this.serviceId) +'/version';
+        var url = '/service/' + encodeURIComponent(this.serviceId) + '/version';
         this.request('GET', url, function (err, versions) {
             if (err) {
                 return cb('Failed to fetch versions: ' + err);
             }
-            var latestVersion = versions.reduce(function (latestVersion, version) {
-                if (!latestVersion) return version;
-                if (version.number > latestVersion.number) return version;
-                return latestVersion;
+            var latestVersion = versions.reduce(function (lateVersion, version) {
+                if (!lateVersion) return version;
+                if (version.number > lateVersion.number) return version;
+                return lateVersion;
             });
             return cb(null, latestVersion);
         });
@@ -63,16 +63,16 @@ module.exports = function (apiKey, serviceId) {
         var postUrl = this.getFastlyAPIPrefix(this.serviceId, version) + '/condition';
         return this.request('PUT', putUrl, condition, function (err, response) {
             if (err && err.statusCode === 404) {
-                this.request('POST', postUrl, condition, function (err, response) {
-                    if (err) {
-                        return cb('Failed while inserting condition \"' + condition.statement + '\": ' + err);
+                this.request('POST', postUrl, condition, function (e, resp) {
+                    if (e) {
+                        return cb('Failed while inserting condition "' + condition.statement + '": ' + e);
                     }
-                    return cb(null, response);
+                    return cb(null, resp);
                 });
                 return;
             }
             if (err) {
-                return cb('Failed to update condition \"' + condition.statement + '\": ' + err);
+                return cb('Failed to update condition "' + condition.statement + '": ' + err);
             }
             return cb(null, response);
         }.bind(this));
@@ -95,11 +95,11 @@ module.exports = function (apiKey, serviceId) {
         var postUrl = this.getFastlyAPIPrefix(this.serviceId, version) + '/header';
         return this.request('PUT', putUrl, header, function (err, response) {
             if (err && err.statusCode === 404) {
-                this.request('POST', postUrl, header, function (err, response) {
-                    if (err) {
-                        return cb('Failed to insert header: ' + err);
+                this.request('POST', postUrl, header, function (e, resp) {
+                    if (e) {
+                        return cb('Failed to insert header: ' + e);
                     }
-                    return cb(null, response);
+                    return cb(null, resp);
                 });
                 return;
             }
@@ -127,11 +127,11 @@ module.exports = function (apiKey, serviceId) {
         var postUrl = this.getFastlyAPIPrefix(this.serviceId, version) + '/response_object';
         return this.request('PUT', putUrl, responseObj, function (err, response) {
             if (err && err.statusCode === 404) {
-                this.request('POST', postUrl, responseObj, function (err, response) {
-                    if (err) {
-                        return cb('Failed to insert response object: ' + err);
+                this.request('POST', postUrl, responseObj, function (e, resp) {
+                    if (e) {
+                        return cb('Failed to insert response object: ' + e);
                     }
-                    return cb(null, response);
+                    return cb(null, resp);
                 });
                 return;
             }
@@ -166,7 +166,7 @@ module.exports = function (apiKey, serviceId) {
         this.request('PUT', url, cb);
     };
 
-    /**
+    /*
      * Upsert a custom vcl file. Attempts a PUT, and falls back
      * to POST if not there already.
      *
@@ -186,16 +186,16 @@ module.exports = function (apiKey, serviceId) {
         return this.request('PUT', url, content, function (err, response) {
             if (err && err.statusCode === 404) {
                 content.name = name;
-                this.request('POST', postUrl, content, function (err, response) {
-                    if (err) {
-                        return cb('Failed while adding custom vcl \"' + name + '\": ' + err);
+                this.request('POST', postUrl, content, function (e, resp) {
+                    if (e) {
+                        return cb('Failed while adding custom vcl "' + name + '": ' + e);
                     }
-                    return cb(null, response);
+                    return cb(null, resp);
                 });
                 return;
             }
             if (err) {
-                return cb('Failed to update custom vcl \"' + name + '\": ' + err);
+                return cb('Failed to update custom vcl "' + name + '": ' + err);
             }
             return cb(null, response);
         }.bind(this));
diff --git a/dev-server/handler.js b/dev-server/handler.js
index 43c12857d..339ceff5c 100644
--- a/dev-server/handler.js
+++ b/dev-server/handler.js
@@ -1,10 +1,12 @@
-/**
+/*
  * Constructor
  */
-function Handler (route) {
+const Handler = function (route) {
     // Handle redirects
     if (route.redirect) {
-        return (req, res) => { res.redirect(route.redirect); };
+        return (req, res) => {
+            res.redirect(route.redirect);
+        };
     }
 
     var url = '/' + route.name + '.html';
@@ -12,9 +14,9 @@ function Handler (route) {
         req.url = url;
         next();
     };
-}
+};
 
-/**
+/*
  * Export a new instance
  */
 module.exports = function (route) {
diff --git a/package.json b/package.json
index 68fec9862..9e9ec7fa4 100644
--- a/package.json
+++ b/package.json
@@ -33,24 +33,27 @@
   "devDependencies": {
     "async": "1.5.2",
     "autoprefixer": "6.3.6",
-    "babel-core": "6.10.4",
-    "babel-eslint": "5.0.4",
-    "babel-loader": "6.2.4",
-    "babel-preset-es2015": "6.9.0",
-    "babel-preset-react": "6.11.1",
+    "babel-cli": "6.26.0",
+    "babel-core": "6.23.1",
+    "babel-eslint": "8.0.2",
+    "babel-loader": "7.1.0",
+    "babel-preset-es2015": "6.22.0",
+    "babel-preset-react": "6.22.0",
     "cheerio": "1.0.0-rc.2",
-    "classnames": "2.1.3",
+    "classnames": "2.2.5",
     "cookie": "0.2.2",
     "copy-webpack-plugin": "0.2.0",
+    "create-react-class": "15.6.2",
     "css-loader": "0.23.1",
-    "eslint": "1.3.1",
+    "eslint": "4.7.1",
+    "eslint-config-scratch": "5.0.0",
     "eslint-plugin-json": "1.2.0",
-    "eslint-plugin-react": "3.3.1",
+    "eslint-plugin-react": "7.4.0",
     "exenv": "1.2.0",
     "fastly": "1.2.1",
     "file-loader": "0.8.4",
-    "formsy-react": "0.18.0",
-    "formsy-react-components": "0.7.1",
+    "formsy-react": "0.19.5",
+    "formsy-react-components": "0.11.1",
     "git-bundle-sha": "0.0.2",
     "glob": "5.0.15",
     "google-libphonenumber": "1.0.21",
@@ -59,6 +62,7 @@
     "json-loader": "0.5.2",
     "json2po-stream": "1.0.3",
     "keymirror": "0.1.1",
+    "lodash.bindall": "4.4.0",
     "lodash.clone": "3.0.3",
     "lodash.defaultsdeep": "3.10.0",
     "lodash.isarray": "3.0.4",
@@ -70,21 +74,22 @@
     "node-sass": "4.6.1",
     "pako": "0.2.8",
     "po2icu": "0.0.2",
-    "postcss-loader": "0.8.2",
+    "postcss-loader": "2.0.10",
+    "prop-types": "15.6.0",
     "raven-js": "3.0.4",
-    "react": "15.1.0",
-    "react-dom": "15.0.1",
+    "react": "15.5.4",
+    "react-dom": "15.5.4",
     "react-intl": "2.1.2",
-    "react-modal": "1.5.2",
-    "react-onclickoutside": "4.1.1",
+    "react-modal": "3.1.11",
+    "react-onclickoutside": "6.7.1",
     "react-redux": "4.4.5",
-    "react-responsive": "1.1.4",
+    "react-responsive": "3.0.0",
     "react-slick": "0.12.2",
-    "react-telephone-input": "3.4.5",
+    "react-telephone-input": "3.8.6",
     "redux": "3.5.2",
     "redux-thunk": "2.0.1",
     "sass-lint": "1.5.1",
-    "sass-loader": "2.0.1",
+    "sass-loader": "6.0.6",
     "scratchr2_translations": "git://github.com/LLK/scratchr2_translations.git#master",
     "slick-carousel": "1.5.8",
     "source-map-support": "0.3.2",
@@ -92,8 +97,8 @@
     "tap": "7.1.2",
     "url-loader": "0.5.6",
     "watch": "0.16.0",
-    "webpack": "1.12.14",
-    "webpack-dev-middleware": "1.2.0",
+    "webpack": "2.7.0",
+    "webpack-dev-middleware": "2.0.4",
     "xhr": "2.2.0"
   },
   "nyc": {
diff --git a/src/.eslintrc.js b/src/.eslintrc.js
new file mode 100644
index 000000000..0abd74d67
--- /dev/null
+++ b/src/.eslintrc.js
@@ -0,0 +1,10 @@
+module.exports = {
+    root: true,
+    extends: ['scratch', 'scratch/es6', 'scratch/react'],
+    env: {
+        browser: true
+    },
+    globals: {
+        process: true
+    }
+};
diff --git a/src/components/accordion/accordion.jsx b/src/components/accordion/accordion.jsx
index e6e921a7b..431705395 100644
--- a/src/components/accordion/accordion.jsx
+++ b/src/components/accordion/accordion.jsx
@@ -1,33 +1,35 @@
-var classNames = require('classnames');
-var React = require('react');
+const bindAll = require('lodash.bindall');
+const classNames = require('classnames');
+const PropTypes = require('prop-types');
+const React = require('react');
 
 require('./accordion.scss');
 
-var Accordion = React.createClass({
-    type: 'Accordion',
-    getDefaultProps: function () {
-        return {
-            titleAs: 'div',
-            contentAs: 'div'
-        };
-    },
-    getInitialState: function () {
-        return {
+class Accordion extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleClick'
+        ]);
+        this.state = {
             isOpen: false
         };
-    },
-    toggleContent: function () {
+    }
+    handleClick (e) {
+        e.preventDefault();
         this.setState({isOpen: !this.state.isOpen});
-    },
-    render: function () {
-        var classes = classNames({
-            'content': true,
-            'open': this.state.isOpen
+    }
+    render () {
+        const classes = classNames({
+            content: true,
+            open: this.state.isOpen
         });
         return (
             <div className="accordion">
-                <this.props.titleAs className="title"
-                     onClick={this.toggleContent}>
+                <this.props.titleAs
+                    className="title"
+                    onClick={this.handleClick}
+                >
                     {this.props.title}
                 </this.props.titleAs>
                 <this.props.contentAs className={classes}>
@@ -36,6 +38,16 @@ var Accordion = React.createClass({
             </div>
         );
     }
-});
+}
+
+Accordion.propTypes = {
+    content: PropTypes.node,
+    title: PropTypes.string
+};
+
+Accordion.defaultProps = {
+    contentAs: 'div',
+    titleAs: 'div'
+};
 
 module.exports = Accordion;
diff --git a/src/components/adminpanel/adminpanel.jsx b/src/components/adminpanel/adminpanel.jsx
index 222258176..4d9ac9e79 100644
--- a/src/components/adminpanel/adminpanel.jsx
+++ b/src/components/adminpanel/adminpanel.jsx
@@ -1,37 +1,39 @@
-var React = require('react');
-var connect = require('react-redux').connect;
+const bindAll = require('lodash.bindall');
+const connect = require('react-redux').connect;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var Button = require('../forms/button.jsx');
+const Button = require('../forms/button.jsx');
 
 require('./adminpanel.scss');
 
-var AdminPanel = React.createClass({
-    type: 'AdminPanel',
-    getInitialState: function () {
-        return {
+class AdminPanel extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleToggleVisibility'
+        ]);
+        this.state = {
             showPanel: false
         };
-    },
-    handleToggleVisibility: function (e) {
+    }
+    handleToggleVisibility (e) {
         e.preventDefault();
         this.setState({showPanel: !this.state.showPanel});
-    },
-    render: function () {
-        // make sure user is present before checking if they're an admin. Don't show anything if user not an admin.
-        var showAdmin = false;
-        if (this.props.session.session.user) {
-            showAdmin = this.props.session.session.permissions.admin;
-        }
-
-        if (!showAdmin) return false;
+    }
+    render () {
+        if (!this.props.isAdmin) return false;
 
         if (this.state.showPanel) {
             return (
-                <div id="admin-panel" className="visible">
+                <div
+                    className="visible"
+                    id="admin-panel"
+                >
                     <span
                         className="toggle"
-                        onClick={this.handleToggleVisibility}>
-
+                        onClick={this.handleToggleVisibility}
+                    >
                         x
                     </span>
                     <div className="admin-header">
@@ -44,8 +46,15 @@ var AdminPanel = React.createClass({
                             <dd>
                                 <ul className="cache-list">
                                     <li>
-                                        <form method="post" action="/scratch_admin/page/clear-anon-cache/">
-                                            <input type="hidden" name="path" value="/" />
+                                        <form
+                                            action="/scratch_admin/page/clear-anon-cache/"
+                                            method="post"
+                                        >
+                                            <input
+                                                name="path"
+                                                type="hidden"
+                                                value="/"
+                                            />
                                             <div className="button-row">
                                                 <span>For anonymous users:</span>
                                                 <Button type="submit">
@@ -53,34 +62,39 @@ var AdminPanel = React.createClass({
                                                 </Button>
                                             </div>
                                         </form>
-                                  </li>
+                                    </li>
                                 </ul>
                             </dd>
                         </dl>
                     </div>
                 </div>
             );
-        } else {
-            return (
-                <div id="admin-panel" className="hidden">
-                    <span
-                        className="toggle"
-                        onClick={this.handleToggleVisibility}>
-
-                        &gt;
-                    </span>
-                </div>
-            );
         }
+        return (
+            <div
+                className="hidden"
+                id="admin-panel"
+            >
+                <span
+                    className="toggle"
+                    onClick={this.handleToggleVisibility}
+                >
+                    &gt;
+                </span>
+            </div>
+        );
     }
-});
+}
 
-var mapStateToProps = function (state) {
-    return {
-        session: state.session
-    };
+AdminPanel.propTypes = {
+    children: PropTypes.node,
+    isAdmin: PropTypes.bool
 };
 
-var ConnectedAdminPanel = connect(mapStateToProps)(AdminPanel);
+const mapStateToProps = state => ({
+    isAdmin: state.permissions.admin
+});
+
+const ConnectedAdminPanel = connect(mapStateToProps)(AdminPanel);
 
 module.exports = ConnectedAdminPanel;
diff --git a/src/components/avatar/avatar.jsx b/src/components/avatar/avatar.jsx
index 5830e5ffb..566b97199 100644
--- a/src/components/avatar/avatar.jsx
+++ b/src/components/avatar/avatar.jsx
@@ -1,23 +1,22 @@
-var React = require('react');
-var classNames = require('classnames');
+const classNames = require('classnames');
+const omit = require('lodash.omit');
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var Avatar = React.createClass({
-    type: 'Avatar',
-    propTypes: {
-        src: React.PropTypes.string
-    },
-    getDefaultProps: function () {
-        return {
-            src: '//cdn2.scratch.mit.edu/get_image/user/2584924_24x24.png?v=1438702210.96'
-        };
-    },
-    render: function () {
-        var classes = classNames(
-            'avatar',
-            this.props.className
-        );
-        return <img {... this.props} className={classes} />;
-    }
-});
+const Avatar = props => (
+    <img
+        className={classNames('avatar', props.className)}
+        {...omit(props, ['className'])}
+    />
+);
+
+Avatar.propTypes = {
+    className: PropTypes.string,
+    src: PropTypes.string
+};
+
+Avatar.defaultProps = {
+    src: '//cdn2.scratch.mit.edu/get_image/user/2584924_24x24.png?v=1438702210.96'
+};
 
 module.exports = Avatar;
diff --git a/src/components/box/box.jsx b/src/components/box/box.jsx
index 543bc15a2..832e27ea4 100644
--- a/src/components/box/box.jsx
+++ b/src/components/box/box.jsx
@@ -1,40 +1,38 @@
-var classNames = require('classnames');
-var React = require('react');
+const classNames = require('classnames');
+const PropTypes = require('prop-types');
+const React = require('react');
 
 require('./box.scss');
 
-var Box = React.createClass({
-    type: 'Box',
-    propTypes: {
-        title: React.PropTypes.string.isRequired,
-        subtitle: React.PropTypes.string,
-        moreTitle: React.PropTypes.string,
-        moreHref: React.PropTypes.string,
-        moreProps: React.PropTypes.object
-    },
-    render: function () {
-        var classes = classNames(
-            'box',
-            this.props.className
-        );
-        return (
-            <div className={classes}>
-                <div className="box-header">
-                    <h4>{this.props.title}</h4>
-                    <h5>{this.props.subtitle}</h5>
-                    <p>
-                        <a href={this.props.moreHref} {...this.props.moreProps}>
-                            {this.props.moreTitle}
-                        </a>
-                    </p>
-                </div>
+const Box = props => (
+    <div className={classNames('box', props.className)}>
+        <div className="box-header">
+            <h4>{props.title}</h4>
+            <h5>{props.subtitle}</h5>
+            <p>
+                <a
+                    href={props.moreHref}
+                    {...props.moreProps}
+                >
+                    {props.moreTitle}
+                </a>
+            </p>
+        </div>
 
-                <div className="box-content">
-                    {this.props.children}
-                </div>
-            </div>
-        );
-    }
-});
+        <div className="box-content">
+            {props.children}
+        </div>
+    </div>
+);
+
+Box.propTypes = {
+    children: PropTypes.node,
+    className: PropTypes.string,
+    moreHref: PropTypes.string,
+    moreProps: PropTypes.object, // eslint-disable-line react/forbid-prop-types
+    moreTitle: PropTypes.string,
+    subtitle: PropTypes.string,
+    title: PropTypes.string.isRequired
+};
 
 module.exports = Box;
diff --git a/src/components/card/card.jsx b/src/components/card/card.jsx
index 5ac22fae1..37f6dc13c 100644
--- a/src/components/card/card.jsx
+++ b/src/components/card/card.jsx
@@ -1,17 +1,18 @@
-var classNames = require('classnames');
-var React = require('react');
+const classNames = require('classnames');
+const PropTypes = require('prop-types');
+const React = require('react');
 
 require('./card.scss');
 
-var Card = React.createClass({
-    displayName: 'Card',
-    render: function () {
-        return (
-            <div className={classNames(['card', this.props.className])}>
-                {this.props.children}
-            </div>
-        );
-    }
-});
+const Card = props => (
+    <div className={classNames(['card', props.className])}>
+        {props.children}
+    </div>
+);
+
+Card.propTypes = {
+    children: PropTypes.node,
+    className: PropTypes.string
+};
 
 module.exports = Card;
diff --git a/src/components/carousel/carousel.jsx b/src/components/carousel/carousel.jsx
index 46e01fa1a..5438d414f 100644
--- a/src/components/carousel/carousel.jsx
+++ b/src/components/carousel/carousel.jsx
@@ -1,95 +1,115 @@
-var classNames = require('classnames');
-var defaults = require('lodash.defaults');
-var React = require('react');
-var Slider = require('react-slick');
+const classNames = require('classnames');
+const defaults = require('lodash.defaults');
+const PropTypes = require('prop-types');
+const React = require('react');
+const Slider = require('react-slick');
 
-var Thumbnail = require('../thumbnail/thumbnail.jsx');
+const Thumbnail = require('../thumbnail/thumbnail.jsx');
 
-var frameless = require('../../lib/frameless.js');
+const frameless = require('../../lib/frameless.js');
 
 require('slick-carousel/slick/slick.scss');
 require('slick-carousel/slick/slick-theme.scss');
 require('./carousel.scss');
 
-/**
- * Displays content in horizontal scrolling box. Example usage: splash page rows.
- */
-var Carousel = React.createClass({
-    type: 'Carousel',
-    propTypes: {
-        items: React.PropTypes.array
-    },
-    getDefaultProps: function () {
-        return {
-            items: require('./carousel.json'),
-            showRemixes: false,
-            showLoves: false,
-            type: 'project'
-        };
-    },
-    render: function () {
-        var settings = this.props.settings || {};
-        defaults(settings, {
-            centerMode: false,
-            dots: false,
-            infinite: false,
-            lazyLoad: true,
-            slidesToShow: 5,
-            slidesToScroll: 5,
-            variableWidth: true,
-            responsive: [
-                {breakpoint: frameless.mobile, settings: {
+const Carousel = props => {
+    defaults(props.settings, {
+        centerMode: false,
+        dots: false,
+        infinite: false,
+        lazyLoad: true,
+        slidesToShow: 5,
+        slidesToScroll: 5,
+        variableWidth: true,
+        responsive: [
+            {
+                breakpoint: frameless.mobile,
+                settings: {
                     arrows: true,
                     slidesToScroll: 1,
                     slidesToShow: 1,
                     centerMode: true
-                }},
-                {breakpoint: frameless.tablet, settings: {
+                }
+            },
+            {
+                breakpoint: frameless.tablet,
+                settings: {
                     slidesToScroll: 2,
                     slidesToShow: 2
-                }},
-                {breakpoint: frameless.desktop, settings: {
+                }
+            },
+            {
+                breakpoint: frameless.desktop,
+                settings: {
                     slidesToScroll: 4,
                     slidesToShow: 4
-                }}
-            ]
-        });
-        var arrows = this.props.items.length > settings.slidesToShow;
-        var classes = classNames(
-            'carousel',
-            this.props.className
-        );
-        return (
-            <Slider className={classes} arrows={arrows} {... settings}>
-                {this.props.items.map(function (item) {
-                    var href = '';
-                    switch (this.props.type) {
-                    case 'gallery':
-                        href = '/studios/' + item.id + '/';
-                        break;
-                    case 'project':
-                        href = '/projects/' + item.id + '/';
-                        break;
-                    default:
-                        href = '/' + item.type + '/' + item.id + '/';
-                    }
+                }
+            }
+        ]
+    });
+    const arrows = props.items.length > props.settings.slidesToShow;
+    return (
+        <Slider
+            arrows={arrows}
+            className={classNames('carousel', props.className)}
+            {... props.settings}
+        >
+            {props.items.map(item => {
+                let href = '';
+                switch (props.type) {
+                case 'gallery':
+                    href = `/studios/${item.id}/`;
+                    break;
+                case 'project':
+                    href = `/projects/${item.id}/`;
+                    break;
+                default:
+                    href = `/${item.type}/${item.id}/`;
+                }
 
-                    return (
-                        <Thumbnail key={[this.key, item.id].join('.')}
-                                   showLoves={this.props.showLoves}
-                                   showRemixes={this.props.showRemixes}
-                                   type={this.props.type}
-                                   href={href}
-                                   title={item.title}
-                                   src={item.image}
-                                   creator={item.author.username}
-                                   remixes={item.stats.remixes}
-                                   loves={item.stats.loves} />
-                    );
-                }.bind(this))}
-            </Slider>
-        );
-    }
-});
+                return (
+                    <Thumbnail
+                        creator={item.author.username}
+                        href={href}
+                        key={[props.type, item.id].join('.')}
+                        loves={item.stats.loves}
+                        remixes={item.stats.remixes}
+                        showLoves={props.showLoves}
+                        showRemixes={props.showRemixes}
+                        src={item.image}
+                        title={item.title}
+                        type={props.type}
+                    />
+                );
+            })}
+        </Slider>
+    );
+};
+
+Carousel.propTypes = {
+    className: PropTypes.string,
+    items: PropTypes.arrayOf(PropTypes.any),
+    settings: PropTypes.shape({
+        centerMode: PropTypes.bool,
+        dots: PropTypes.bool,
+        infinite: PropTypes.bool,
+        lazyLoad: PropTypes.bool,
+        slidesToShow: PropTypes.number,
+        slidesToScroll: PropTypes.number,
+        variableWidth: PropTypes.bool,
+        responsive: PropTypes.array
+    }),
+    showLoves: PropTypes.bool,
+    showRemixes: PropTypes.bool,
+    type: PropTypes.string
+};
+
+Carousel.defaultProps = {
+    items: require('./carousel.json'),
+    settings: {},
+    showRemixes: false,
+    showLoves: false,
+    type: 'project'
+};
 
 module.exports = Carousel;
diff --git a/src/components/carousel/legacy-carousel.jsx b/src/components/carousel/legacy-carousel.jsx
index 36efb5cbf..7e29f2dd2 100644
--- a/src/components/carousel/legacy-carousel.jsx
+++ b/src/components/carousel/legacy-carousel.jsx
@@ -1,97 +1,118 @@
 // This component handles json returned via proxy from a django server,
 // or directly from a django server, and the model structure that system
 // has.
-var classNames = require('classnames');
-var defaults = require('lodash.defaults');
-var React = require('react');
-var Slider = require('react-slick');
+const classNames = require('classnames');
+const defaults = require('lodash.defaults');
+const PropTypes = require('prop-types');
+const React = require('react');
+const Slider = require('react-slick');
 
-var Thumbnail = require('../thumbnail/thumbnail.jsx');
+const Thumbnail = require('../thumbnail/thumbnail.jsx');
 
-var frameless = require('../../lib/frameless.js');
+const frameless = require('../../lib/frameless.js');
 
 require('slick-carousel/slick/slick.scss');
 require('slick-carousel/slick/slick-theme.scss');
 require('./carousel.scss');
 
-/**
- * Displays content in horizontal scrolling box. Example usage: splash page rows.
- */
-var LegacyCarousel = React.createClass({
-    type: 'LegacyCarousel',
-    propTypes: {
-        items: React.PropTypes.array
-    },
-    getDefaultProps: function () {
-        return {
-            items: require('./carousel.json'),
-            showRemixes: false,
-            showLoves: false
-        };
-    },
-    render: function () {
-        var settings = this.props.settings || {};
-        defaults(settings, {
-            centerMode: false,
-            dots: false,
-            infinite: false,
-            lazyLoad: true,
-            slidesToShow: 5,
-            slidesToScroll: 5,
-            variableWidth: true,
-            responsive: [
-                {breakpoint: frameless.mobile, settings: {
+const Carousel = props => {
+    defaults(props.settings, {
+        centerMode: false,
+        dots: false,
+        infinite: false,
+        lazyLoad: true,
+        slidesToShow: 5,
+        slidesToScroll: 5,
+        variableWidth: true,
+        responsive: [
+            {
+                breakpoint: frameless.mobile,
+                settings: {
                     arrows: true,
                     slidesToScroll: 1,
                     slidesToShow: 1,
                     centerMode: true
-                }},
-                {breakpoint: frameless.tablet, settings: {
+                }
+            },
+            {
+                breakpoint: frameless.tablet,
+                settings: {
                     slidesToScroll: 2,
                     slidesToShow: 2
-                }},
-                {breakpoint: frameless.desktop, settings: {
+                }
+            },
+            {
+                breakpoint: frameless.desktop,
+                settings: {
                     slidesToScroll: 4,
                     slidesToShow: 4
-                }}
-            ]
-        });
-        var arrows = this.props.items.length > settings.slidesToShow;
-        var classes = classNames(
-            'carousel',
-            this.props.className
-        );
-        return (
-            <Slider className={classes} arrows={arrows} {... settings}>
-                {this.props.items.map(function (item) {
-                    var href = '';
-                    switch (item.type) {
-                    case 'gallery':
-                        href = '/studios/' + item.id + '/';
-                        break;
-                    case 'project':
-                        href = '/projects/' + item.id + '/';
-                        break;
-                    default:
-                        href = '/' + item.type + '/' + item.id + '/';
-                    }
+                }
+            }
+        ]
+    });
+    const arrows = props.items.length > props.settings.slidesToShow;
+    return (
+        <Slider
+            arrows={arrows}
+            className={classNames('carousel', props.className)}
+            {... props.settings}
+        >
+            {props.items.map(item => {
+                let href = '';
+                switch (props.type) {
+                case 'gallery':
+                    href = `/studios/${item.id}/`;
+                    break;
+                case 'project':
+                    href = `/projects/${item.id}/`;
+                    break;
+                default:
+                    href = `/${item.type}/${item.id}/`;
+                }
 
-                    return (
-                        <Thumbnail key={[this.key, item.id].join('.')}
-                                   showLoves={this.props.showLoves}
-                                   showRemixes={this.props.showRemixes}
-                                   type={item.type}
-                                   href={href}
-                                   title={item.title}
-                                   src={item.thumbnail_url}
-                                   creator={item.creator}
-                                   remixes={item.remixers_count}
-                                   loves={item.love_count} />
-                    );
-                }.bind(this))}
-            </Slider>
-        );
-    }
-});
+                return (
+                    <Thumbnail
+                        creator={item.creator}
+                        href={href}
+                        key={[props.type, item.id].join('.')}
+                        loves={item.love_count}
+                        remixes={item.remixers_count}
+                        showLoves={props.showLoves}
+                        showRemixes={props.showRemixes}
+                        src={item.thumbnail_url}
+                        title={item.title}
+                        type={item.type}
+                    />
+                );
+            })}
+        </Slider>
+    );
+};
 
-module.exports = LegacyCarousel;
+Carousel.propTypes = {
+    className: PropTypes.string,
+    items: PropTypes.arrayOf(PropTypes.any),
+    settings: PropTypes.shape({
+        centerMode: PropTypes.bool,
+        dots: PropTypes.bool,
+        infinite: PropTypes.bool,
+        lazyLoad: PropTypes.bool,
+        slidesToShow: PropTypes.number,
+        slidesToScroll: PropTypes.number,
+        variableWidth: PropTypes.bool,
+        responsive: PropTypes.array
+    }),
+    showLoves: PropTypes.bool,
+    showRemixes: PropTypes.bool,
+    type: PropTypes.string
+};
+
+Carousel.defaultProps = {
+    items: require('./carousel.json'),
+    settings: {},
+    showRemixes: false,
+    showLoves: false,
+    type: 'project'
+};
+
+module.exports = Carousel;
diff --git a/src/components/comment/comment.jsx b/src/components/comment/comment.jsx
index 4a324799a..5edaeb51d 100644
--- a/src/components/comment/comment.jsx
+++ b/src/components/comment/comment.jsx
@@ -1,34 +1,33 @@
-var classNames = require('classnames');
-var FormattedRelative = require('react-intl').FormattedRelative;
-var React = require('react');
+const classNames = require('classnames');
+const FormattedRelative = require('react-intl').FormattedRelative;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var EmojiText = require('../emoji-text/emoji-text.jsx');
+const EmojiText = require('../emoji-text/emoji-text.jsx');
 
 require('./comment.scss');
 
-var CommentText = React.createClass({
-    type: 'CommentText',
-    propTypes: {
-        comment: React.PropTypes.string.isRequired,
-        datetimeCreated: React.PropTypes.string,
-        className: React.PropTypes.string
-    },
-    render: function () {
-        var classes = classNames(
-            'comment-text',
-            this.props.class
-        );
-        return (
-            <div className={classes}>
-                <EmojiText className="mod-comment" text={this.props.comment} />
-                {typeof this.props.datetimeCreated !== 'undefined' ? [
-                    <p className="comment-text-timestamp">
-                        <FormattedRelative value={new Date(this.props.datetimeCreated)} />
-                    </p>
-                ] : []}
-            </div>
-        );
-    }
-});
+const CommentText = props => (
+    <div className={classNames('comment-text', props.className)}>
+        <EmojiText
+            className="mod-comment"
+            text={props.comment}
+        />
+        {typeof props.datetimeCreated === 'undefined' ? [] : [
+            <p
+                className="comment-text-timestamp"
+                key="comment-text-timestamp"
+            >
+                <FormattedRelative value={new Date(props.datetimeCreated)} />
+            </p>
+        ]}
+    </div>
+);
+
+CommentText.propTypes = {
+    className: PropTypes.string,
+    comment: PropTypes.string.isRequired,
+    datetimeCreated: PropTypes.string
+};
 
 module.exports = CommentText;
diff --git a/src/components/deck/deck.jsx b/src/components/deck/deck.jsx
index 5d929c68a..7d150a7e8 100644
--- a/src/components/deck/deck.jsx
+++ b/src/components/deck/deck.jsx
@@ -1,22 +1,29 @@
-var classNames = require('classnames');
-var React = require('react');
+const classNames = require('classnames');
+const PropTypes = require('prop-types');
+const React = require('react');
 
 require('./deck.scss');
 
-var Deck = React.createClass({
-    displayName: 'Deck',
-    render: function () {
-        return (
-            <div className={classNames(['deck', this.props.className])}>
-                <div className="inner">
-                    <a href="/" aria-label="Scratch">
-                        <img className="logo" src="/images/logo_sm.png" />
-                    </a>
-                    {this.props.children}
-                </div>
-            </div>
-        );
-    }
-});
+const Deck = props => (
+    <div className={classNames(['deck', props.className])}>
+        <div className="inner">
+            <a
+                aria-label="Scratch"
+                href="/"
+            >
+                <img
+                    className="logo"
+                    src="/images/logo_sm.png"
+                />
+            </a>
+            {props.children}
+        </div>
+    </div>
+);
+
+Deck.propTypes = {
+    children: PropTypes.node,
+    className: PropTypes.string
+};
 
 module.exports = Deck;
diff --git a/src/components/dropdown-banner/banner.jsx b/src/components/dropdown-banner/banner.jsx
index e9808228e..fa912f49e 100644
--- a/src/components/dropdown-banner/banner.jsx
+++ b/src/components/dropdown-banner/banner.jsx
@@ -1,33 +1,29 @@
-var classNames = require('classnames');
-var React = require('react');
+const classNames = require('classnames');
+const PropTypes = require('prop-types');
+const React = require('react');
 
 require('./banner.scss');
 
-/**
- * Container for messages displayed below the nav bar that can be dismissed
- * (See: email not confirmed banner)
- */
-var Banner = React.createClass({
-    type: 'Banner',
-    propTypes: {
-        onRequestDismiss: React.PropTypes.func
-    },
-    render: function () {
-        var classes = classNames(
-            'banner',
-            this.props.className
-        );
-        return (
-            <div className={classes}>
-                <div className="inner">
-                    {this.props.children}
-                    {this.props.onRequestDismiss ? [
-                        <a className="close" key="close" href="#" onClick={this.props.onRequestDismiss}>x</a>
-                    ] : []}
-                </div>
-            </div>
-        );
-    }
-});
+const Banner = props => (
+    <div className={classNames('banner', props.className)}>
+        <div className="inner">
+            {props.children}
+            {props.onRequestDismiss ? [
+                <a
+                    className="close"
+                    href="#"
+                    key="close"
+                    onClick={props.onRequestDismiss}
+                >x</a>
+            ] : []}
+        </div>
+    </div>
+);
+
+Banner.propTypes = {
+    children: PropTypes.node,
+    className: PropTypes.string,
+    onRequestDismiss: PropTypes.func
+};
 
 module.exports = Banner;
diff --git a/src/components/dropdown/dropdown.jsx b/src/components/dropdown/dropdown.jsx
index 83990264b..d4a3681e1 100644
--- a/src/components/dropdown/dropdown.jsx
+++ b/src/components/dropdown/dropdown.jsx
@@ -1,40 +1,46 @@
-var React = require('react');
-var classNames = require('classnames');
+const bindAll = require('lodash.bindall');
+const classNames = require('classnames');
+const onClickOutside = require('react-onclickoutside').default;
+const PropTypes = require('prop-types');
+const React = require('react');
 
 require('./dropdown.scss');
 
-var Dropdown = React.createClass({
-    type: 'Dropdown',
-    mixins: [
-        require('react-onclickoutside')
-    ],
-    propTypes: {
-        onRequestClose: React.PropTypes.func,
-        isOpen: React.PropTypes.bool
-    },
-    getDefaultProps: function () {
-        return {
-            as: 'div',
-            isOpen: false
-        };
-    },
-    handleClickOutside: function () {
+class Dropdown extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleClickOutside'
+        ]);
+    }
+    handleClickOutside () {
         if (this.props.isOpen) {
             this.props.onRequestClose();
         }
-    },
-    render: function () {
-        var classes = classNames(
-            'dropdown',
-            this.props.className,
-            {open: this.props.isOpen}
-        );
+    }
+    render () {
         return (
-            <this.props.as className={classes}>
+            <this.props.as
+                className={classNames('dropdown', this.props.className, {
+                    [open]: this.props.isOpen
+                })}
+            >
                 {this.props.children}
             </this.props.as>
         );
     }
-});
+}
 
-module.exports = Dropdown;
+Dropdown.propTypes = {
+    children: PropTypes.node,
+    className: PropTypes.string,
+    isOpen: PropTypes.bool,
+    onRequestClose: PropTypes.func.isRequired
+};
+
+Dropdown.defaultProps = {
+    as: 'div',
+    isOpen: false
+};
+
+module.exports = onClickOutside(Dropdown);
diff --git a/src/components/emoji-text/emoji-text.jsx b/src/components/emoji-text/emoji-text.jsx
index c6f382260..ee4c00a52 100644
--- a/src/components/emoji-text/emoji-text.jsx
+++ b/src/components/emoji-text/emoji-text.jsx
@@ -1,33 +1,25 @@
-var classNames = require('classnames');
-var React = require('react');
+const classNames = require('classnames');
+const PropTypes = require('prop-types');
+const React = require('react');
 
 require('./emoji-text.scss');
 
-var EmojiText = React.createClass({
-    type: 'EmojiText',
-    propTyes: {
-        text: React.PropTypes.string.isRequired,
-        className: React.PropTypes.string
-    },
-    getDefaultProps: function () {
-        return {
-            as: 'p'
-        };
-    },
-    render: function () {
-        var classes = classNames(
-            'emoji-text',
-            this.props.className
-        );
-        return (
-            <this.props.as
-                className={classes}
-                dangerouslySetInnerHTML={{
-                    __html: this.props.text
-                }}
-            />
-        );
-    }
-});
+const EmojiText = props => (
+    <props.as
+        className={classNames('emoji-text', props.className)}
+        dangerouslySetInnerHTML={{ // eslint-disable-line react/no-danger
+            __html: props.text
+        }}
+    />
+);
+
+EmojiText.propTypes = {
+    className: PropTypes.string,
+    text: PropTypes.string.isRequired
+};
+
+EmojiText.defaultProps = {
+    as: 'p'
+};
 
 module.exports = EmojiText;
diff --git a/src/components/flex-row/flex-row.jsx b/src/components/flex-row/flex-row.jsx
index 4a2deb36e..4dc8582c6 100644
--- a/src/components/flex-row/flex-row.jsx
+++ b/src/components/flex-row/flex-row.jsx
@@ -1,26 +1,22 @@
-var classNames = require('classnames');
-var React = require('react');
+const classNames = require('classnames');
+const PropTypes = require('prop-types');
+const React = require('react');
 
 require('./flex-row.scss');
 
-var FlexRow = React.createClass({
-    type: 'FlexRow',
-    getDefaultProps: function () {
-        return {
-            as: 'div'
-        };
-    },
-    render: function () {
-        var classes = classNames(
-            'flex-row',
-            this.props.className
-        );
-        return (
-            <this.props.as className={classes}>
-                {this.props.children}
-            </this.props.as>
-        );
-    }
-});
+const FlexRow = props => (
+    <props.as className={classNames('flex-row', props.className)}>
+        {props.children}
+    </props.as>
+);
+
+FlexRow.propTypes = {
+    children: PropTypes.node,
+    className: PropTypes.string
+};
+
+FlexRow.defaultProps = {
+    as: 'div'
+};
 
 module.exports = FlexRow;
diff --git a/src/components/footer/conference/2016/footer.jsx b/src/components/footer/conference/2016/footer.jsx
index b342e59fd..76207f6af 100644
--- a/src/components/footer/conference/2016/footer.jsx
+++ b/src/components/footer/conference/2016/footer.jsx
@@ -1,125 +1,152 @@
-var React = require('react');
+const React = require('react');
 
-var FlexRow = require('../../../flex-row/flex-row.jsx');
-var FooterBox = require('../../container/footer.jsx');
+const FlexRow = require('../../../flex-row/flex-row.jsx');
+const FooterBox = require('../../container/footer.jsx');
 
 require('../footer.scss');
 
-var ConferenceFooter = React.createClass({
-    type: 'ConferenceFooter',
-    render: function () {
-        return (
-            <FooterBox>
-                <div className="collaborators">
-                    <h4>Sponsors</h4>
+const ConferenceFooter = () => (
+    <FooterBox>
+        <div className="collaborators">
+            <h4>Sponsors</h4>
+            <FlexRow as="ul">
+                <li className="odl">
+                    <a href="https://odl.mit.edu/">
+                        <img
+                            alt="MIT Office of Digital Learning"
+                            src="/images/conference/footer/mit-odl.png"
+                        />
+                    </a>
+                </li>
+                <li className="intel">
+                    <a href="http://www.intel.com/content/www/us/en/homepage.html">
+                        <img
+                            alt="Intel"
+                            src="/images/conference/footer/intel.png"
+                        />
+                    </a>
+                </li>
+                <li className="lego">
+                    <a href="http://www.legofoundation.com/">
+                        <img
+                            alt="LEGO Foundation"
+                            src="/images/conference/footer/lego-foundation.png"
+                        />
+                    </a>
+                </li>
+                <li className="google">
+                    <a href="http://www.google.com/">
+                        <img
+                            alt="Google"
+                            src="/images/conference/footer/google.png"
+                        />
+                    </a>
+                </li>
+                <li className="siegel">
+                    <a href="http://www.siegelendowment.org/">
+                        <img
+                            alt="Siegel Family Endowment"
+                            src="/images/conference/footer/siegel-endowment.png"
+                        />
+                    </a>
+                </li>
+                <li className="nostarch">
+                    <a href="https://www.nostarch.com/">
+                        <img
+                            alt="No Starch Press"
+                            src="/images/conference/footer/no-starch.png"
+                        />
+                    </a>
+                </li>
+                <li className="scratchfoundation">
+                    <a href="http://www.scratchfoundation.org/">
+                        <img
+                            alt="Scratch Foundation"
+                            src="/images/conference/footer/scratch-foundation.png"
+                        />
+                    </a>
+                </li>
+            </FlexRow>
+        </div>
+        <FlexRow className="scratch-links">
+            <div className="family">
+                <h4>Scratch Family</h4>
+                <FlexRow>
+                    <FlexRow
+                        as="ul"
+                        className="column"
+                    >
+                        <li>
+                            <a href="https://scratch.mit.edu">Scratch</a>
+                        </li>
+                        <li>
+                            <a href="http://www.scratchjr.org/">ScratchJr</a>
+                        </li>
+                    </FlexRow>
+                    <FlexRow
+                        as="ul"
+                        className="column"
+                    >
+                        <li>
+                            <a href="http://www.scratchfoundation.org/">Scratch Foundation</a>
+                        </li>
+                        <li>
+                            <a href="http://scratched.gse.harvard.edu/">ScratchEd</a>
+                        </li>
+                    </FlexRow>
+                    <FlexRow
+                        as="ul"
+                        className="column"
+                    >
+                        <li>
+                            <a href="http://day.scratch.mit.edu">Scratch Day</a>
+                        </li>
+                    </FlexRow>
+                </FlexRow>
+                <p className="legal">
+                    Scratch is a project of the Lifelong Kindergarten Group at the MIT Media Lab.
+                </p>
+            </div>
+            <div className="media">
+                <div className="contact-us">
+                    <h4>Contact</h4>
+                    <p>
+                        <a href="mailto:help@scratch.mit.edu">
+                            Email Us
+                        </a>
+                    </p>
+                </div>
+                <div className="social">
                     <FlexRow as="ul">
-                        <li className="odl">
-                            <a href="https://odl.mit.edu/">
-                                <img src="/images/conference/footer/mit-odl.png"
-                                     alt="MIT Office of Digital Learning" />
+                        <li>
+                            <a href="//www.twitter.com/scratch">
+                                <img
+                                    alt="scratch twitter"
+                                    src="/images/conference/footer/twitter.png"
+                                />
                             </a>
                         </li>
-                        <li className="intel">
-                            <a href="http://www.intel.com/content/www/us/en/homepage.html">
-                                <img src="/images/conference/footer/intel.png"
-                                     alt="Intel" />
+                        <li>
+                            <a href="//www.facebook.com/scratchteam">
+                                <img
+                                    alt="scratch facebook"
+                                    src="/images/conference/footer/facebook.png"
+                                />
                             </a>
                         </li>
-                        <li className="lego">
-                            <a href="http://www.legofoundation.com/">
-                                <img src="/images/conference/footer/lego-foundation.png"
-                                     alt="LEGO Foundation" />
-                            </a>
-                        </li>
-                        <li className="google">
-                            <a href="http://www.google.com/">
-                                <img src="/images/conference/footer/google.png"
-                                     alt="Google" />
-                            </a>
-                        </li>
-                        <li className="siegel">
-                            <a href="http://www.siegelendowment.org/">
-                                <img src="/images/conference/footer/siegel-endowment.png"
-                                     alt="Siegel Family Endowment" />
-                            </a>
-                        </li>
-                        <li className="nostarch">
-                            <a href="https://www.nostarch.com/">
-                                <img src="/images/conference/footer/no-starch.png"
-                                     alt="No Starch Press" />
-                            </a>
-                        </li>
-                        <li className="scratchfoundation">
-                            <a href="http://www.scratchfoundation.org/">
-                                <img src="/images/conference/footer/scratch-foundation.png"
-                                     alt="Scratch Foundation" />
+                        <li>
+                            <a href="http://medium.com/scratchfoundation-blog">
+                                <img
+                                    alt="scratch foundation blog"
+                                    src="/images/conference/footer/medium.png"
+                                />
                             </a>
                         </li>
                     </FlexRow>
                 </div>
-                <FlexRow className="scratch-links">
-                    <div className="family">
-                        <h4>Scratch Family</h4>
-                        <FlexRow>
-                            <FlexRow as="ul" className="column">
-                                <li>
-                                    <a href="https://scratch.mit.edu" target="_blank">Scratch</a>
-                                </li>
-                                <li>
-                                    <a href="http://www.scratchjr.org/" target="_blank">ScratchJr</a>
-                                </li>
-                            </FlexRow>
-                            <FlexRow as="ul" className="column">
-                                <li>
-                                    <a href="http://www.scratchfoundation.org/" target="_blank">Scratch Foundation</a>
-                                </li>
-                                <li>
-                                    <a href="http://scratched.gse.harvard.edu/" target="_blank">ScratchEd</a>
-                                </li>
-                            </FlexRow>
-                            <FlexRow as="ul" className="column">
-                                <li>
-                                    <a href="http://day.scratch.mit.edu" target="_blank">Scratch Day</a>
-                                </li>
-                            </FlexRow>
-                        </FlexRow>
-                        <p className="legal">
-                            Scratch is a project of the Lifelong Kindergarten Group at the MIT Media Lab.
-                        </p>
-                    </div>
-                    <div className="media">
-                        <div className="contact-us">
-                            <h4>Contact</h4>
-                            <p>
-                                <a href="mailto:help@scratch.mit.edu" target="_blank">
-                                    Email Us
-                                </a>
-                            </p>
-                        </div>
-                        <div className="social">
-                            <FlexRow as="ul">
-                                <li>
-                                    <a href="//www.twitter.com/scratch" target="_blank">
-                                        <img src="/images/conference/footer/twitter.png" alt="scratch twitter" />
-                                    </a>
-                                </li>
-                                <li>
-                                    <a href="//www.facebook.com/scratchteam" target="_blank">
-                                        <img src="/images/conference/footer/facebook.png" alt="scratch facebook" />
-                                    </a>
-                                </li>
-                                <li>
-                                    <a href="http://medium.com/scratchfoundation-blog" target="_blank">
-                                        <img src="/images/conference/footer/medium.png" alt="scratch foundation blog" />
-                                    </a>
-                                </li>
-                            </FlexRow>
-                        </div>
-                    </div>
-                </FlexRow>
-            </FooterBox>
-        );
-    }
-});
+            </div>
+        </FlexRow>
+    </FooterBox>
+);
 
 module.exports = ConferenceFooter;
diff --git a/src/components/footer/conference/2017/footer.jsx b/src/components/footer/conference/2017/footer.jsx
index ebd11f36d..d026ce7ee 100644
--- a/src/components/footer/conference/2017/footer.jsx
+++ b/src/components/footer/conference/2017/footer.jsx
@@ -1,84 +1,100 @@
-var React = require('react');
-var ReactIntl = require('react-intl');
+const injectIntl = require('react-intl').injectIntl;
+const intlShape = require('react-intl').intlShape;
+const FormattedMessage = require('react-intl').FormattedMessage;
+const React = require('react');
 
-var injectIntl = ReactIntl.injectIntl;
-var FormattedMessage = ReactIntl.FormattedMessage;
-
-var FlexRow = require('../../../flex-row/flex-row.jsx');
-var FooterBox = require('../../container/footer.jsx');
-var LanguageChooser = require('../../../languagechooser/languagechooser.jsx');
+const FlexRow = require('../../../flex-row/flex-row.jsx');
+const FooterBox = require('../../container/footer.jsx');
+const LanguageChooser = require('../../../languagechooser/languagechooser.jsx');
 
 require('../footer.scss');
 
-var ConferenceFooter = React.createClass({
-    type: 'ConferenceFooter',
-    render: function () {
-        return (
-            <FooterBox>
-                <FlexRow className="scratch-links">
-                    <div className="family">
-                        <h4><FormattedMessage id='footer.scratchFamily' /></h4>
-                        <FlexRow>
-                            <FlexRow as="ul" className="column">
-                                <li>
-                                    <a href="https://scratch.mit.edu" target="_blank">Scratch</a>
-                                </li>
-                                <li>
-                                    <a href="http://www.scratchjr.org/" target="_blank">ScratchJr</a>
-                                </li>
-                            </FlexRow>
-                            <FlexRow as="ul" className="column">
-                                <li>
-                                    <a href="http://www.scratchfoundation.org/" target="_blank">Scratch Foundation</a>
-                                </li>
-                                <li>
-                                    <a href="http://scratched.gse.harvard.edu/" target="_blank">ScratchEd</a>
-                                </li>
-                            </FlexRow>
-                            <FlexRow as="ul" className="column">
-                                <li>
-                                    <a href="http://day.scratch.mit.edu" target="_blank">Scratch Day</a>
-                                </li>
-                            </FlexRow>
-                        </FlexRow>
-                        <p className="legal">
-                            <FormattedMessage id='general.copyright' />
-                        </p>
-                    </div>
-                    <div className="media">
-                        <div className="contact-us">
-                            <h4>Contact</h4>
-                            <p>
-                                <a href="mailto:help@scratch.mit.edu" target="_blank">
-                                    Email Us
-                                </a>
-                            </p>
-                        </div>
-                        <div className="social">
-                            <FlexRow as="ul">
-                                <li>
-                                    <a href="//www.twitter.com/scratch" target="_blank">
-                                        <img src="/images/conference/footer/twitter.png" alt="scratch twitter" />
-                                    </a>
-                                </li>
-                                <li>
-                                    <a href="//www.facebook.com/scratchteam" target="_blank">
-                                        <img src="/images/conference/footer/facebook.png" alt="scratch facebook" />
-                                    </a>
-                                </li>
-                                <li>
-                                    <a href="http://medium.com/scratchfoundation-blog" target="_blank">
-                                        <img src="/images/conference/footer/medium.png" alt="scratch foundation blog" />
-                                    </a>
-                                </li>
-                            </FlexRow>
-                        </div>
-                    </div>
+const ConferenceFooter = props => (
+    <FooterBox>
+        <FlexRow className="scratch-links">
+            <div className="family">
+                <h4><FormattedMessage id="footer.scratchFamily" /></h4>
+                <FlexRow>
+                    <FlexRow
+                        as="ul"
+                        className="column"
+                    >
+                        <li>
+                            <a href="https://scratch.mit.edu">Scratch</a>
+                        </li>
+                        <li>
+                            <a href="http://www.scratchjr.org/">ScratchJr</a>
+                        </li>
+                    </FlexRow>
+                    <FlexRow
+                        as="ul"
+                        className="column"
+                    >
+                        <li>
+                            <a href="http://www.scratchfoundation.org/">Scratch Foundation</a>
+                        </li>
+                        <li>
+                            <a href="http://scratched.gse.harvard.edu/">ScratchEd</a>
+                        </li>
+                    </FlexRow>
+                    <FlexRow
+                        as="ul"
+                        className="column"
+                    >
+                        <li>
+                            <a href="http://day.scratch.mit.edu">Scratch Day</a>
+                        </li>
+                    </FlexRow>
                 </FlexRow>
-                <LanguageChooser locale={this.props.intl.locale} />
-            </FooterBox>
-        );
-    }
-});
+                <p className="legal">
+                    <FormattedMessage id="general.copyright" />
+                </p>
+            </div>
+            <div className="media">
+                <div className="contact-us">
+                    <h4>Contact</h4>
+                    <p>
+                        <a href="mailto:help@scratch.mit.edu">
+                            Email Us
+                        </a>
+                    </p>
+                </div>
+                <div className="social">
+                    <FlexRow as="ul">
+                        <li>
+                            <a href="//www.twitter.com/scratch">
+                                <img
+                                    alt="scratch twitter"
+                                    src="/images/conference/footer/twitter.png"
+                                />
+                            </a>
+                        </li>
+                        <li>
+                            <a href="//www.facebook.com/scratchteam">
+                                <img
+                                    alt="scratch facebook"
+                                    src="/images/conference/footer/facebook.png"
+                                />
+                            </a>
+                        </li>
+                        <li>
+                            <a href="http://medium.com/scratchfoundation-blog">
+                                <img
+                                    alt="scratch foundation blog"
+                                    src="/images/conference/footer/medium.png"
+                                />
+                            </a>
+                        </li>
+                    </FlexRow>
+                </div>
+            </div>
+        </FlexRow>
+        <LanguageChooser locale={props.intl.locale} />
+    </FooterBox>
+);
+
+ConferenceFooter.propTypes = {
+    intl: intlShape
+};
 
 module.exports = injectIntl(ConferenceFooter);
diff --git a/src/components/footer/conference/2018/footer.jsx b/src/components/footer/conference/2018/footer.jsx
index 06daa2e92..1a08cc823 100644
--- a/src/components/footer/conference/2018/footer.jsx
+++ b/src/components/footer/conference/2018/footer.jsx
@@ -1,84 +1,146 @@
-var React = require('react');
-var ReactIntl = require('react-intl');
+const FormattedMessage = require('react-intl').FormattedMessage;
+const injectIntl = require('react-intl').injectIntl;
+const intlShape = require('react-intl').intlShape;
+const React = require('react');
 
-var injectIntl = ReactIntl.injectIntl;
-var FormattedMessage = ReactIntl.FormattedMessage;
-
-var FlexRow = require('../../../flex-row/flex-row.jsx');
-var FooterBox = require('../../container/footer.jsx');
-var LanguageChooser = require('../../../languagechooser/languagechooser.jsx');
+const FlexRow = require('../../../flex-row/flex-row.jsx');
+const FooterBox = require('../../container/footer.jsx');
+const LanguageChooser = require('../../../languagechooser/languagechooser.jsx');
 
 require('../footer.scss');
 
-var ConferenceFooter = React.createClass({
-    type: 'ConferenceFooter',
-    render: function () {
-        return (
-            <FooterBox>
-                <FlexRow className="scratch-links">
-                    <div className="family">
-                        <h4><FormattedMessage id='footer.scratchFamily' /></h4>
-                        <FlexRow>
-                            <FlexRow as="ul" className="column">
-                                <li>
-                                    <a href="https://scratch.mit.edu" target="_blank">Scratch</a>
-                                </li>
-                                <li>
-                                    <a href="http://www.scratchjr.org/" target="_blank">ScratchJr</a>
-                                </li>
-                            </FlexRow>
-                            <FlexRow as="ul" className="column">
-                                <li>
-                                    <a href="http://www.scratchfoundation.org/" target="_blank">Scratch Foundation</a>
-                                </li>
-                                <li>
-                                    <a href="http://scratched.gse.harvard.edu/" target="_blank">ScratchEd</a>
-                                </li>
-                            </FlexRow>
-                            <FlexRow as="ul" className="column">
-                                <li>
-                                    <a href="http://day.scratch.mit.edu" target="_blank">Scratch Day</a>
-                                </li>
-                            </FlexRow>
-                        </FlexRow>
-                        <p className="legal">
-                            <FormattedMessage id='general.copyright' />
-                        </p>
-                    </div>
-                    <div className="media">
-                        <div className="contact-us">
-                            <h4>Contact</h4>
-                            <p>
-                                <a href="mailto:conference@scratch.mit.edu" target="_blank">
-                                    Email Us
-                                </a>
-                            </p>
-                        </div>
-                        <div className="social">
-                            <FlexRow as="ul">
-                                <li>
-                                    <a href="//www.twitter.com/scratch" target="_blank">
-                                        <img src="/images/conference/footer/twitter.png" alt="scratch twitter" />
-                                    </a>
-                                </li>
-                                <li>
-                                    <a href="//www.facebook.com/scratchteam" target="_blank">
-                                        <img src="/images/conference/footer/facebook.png" alt="scratch facebook" />
-                                    </a>
-                                </li>
-                                <li>
-                                    <a href="http://medium.com/scratchfoundation-blog" target="_blank">
-                                        <img src="/images/conference/footer/medium.png" alt="scratch foundation blog" />
-                                    </a>
-                                </li>
-                            </FlexRow>
-                        </div>
-                    </div>
+const ConferenceFooter = props => (
+    <FooterBox>
+        <FlexRow className="scratch-links">
+            <div className="family">
+                <h4><FormattedMessage id="footer.scratchFamily" /></h4>
+                <FlexRow>
+                    <FlexRow
+                        as="ul"
+                        className="column"
+                    >
+                        <li>
+                            <a
+                                href="https://scratch.mit.edu"
+                                rel="noopener noreferrer"
+                                target="_blank"
+                            >
+                                Scratch
+                            </a>
+                        </li>
+                        <li>
+                            <a
+                                href="http://www.scratchjr.org/"
+                                rel="noopener noreferrer"
+                                target="_blank"
+                            >
+                                ScratchJr
+                            </a>
+                        </li>
+                    </FlexRow>
+                    <FlexRow
+                        as="ul"
+                        className="column"
+                    >
+                        <li>
+                            <a
+                                href="http://www.scratchfoundation.org/"
+                                rel="noopener noreferrer"
+                                target="_blank"
+                            >
+                                Scratch Foundation
+                            </a>
+                        </li>
+                        <li>
+                            <a
+                                href="http://scratched.gse.harvard.edu/"
+                                rel="noopener noreferrer"
+                                target="_blank"
+                            >
+                                ScratchEd
+                            </a>
+                        </li>
+                    </FlexRow>
+                    <FlexRow
+                        as="ul"
+                        className="column"
+                    >
+                        <li>
+                            <a
+                                href="http://day.scratch.mit.edu"
+                                rel="noopener noreferrer"
+                                target="_blank"
+                            >
+                                Scratch Day
+                            </a>
+                        </li>
+                    </FlexRow>
                 </FlexRow>
-                <LanguageChooser locale={this.props.intl.locale} />
-            </FooterBox>
-        );
-    }
-});
+                <p className="legal">
+                    <FormattedMessage id="general.copyright" />
+                </p>
+            </div>
+            <div className="media">
+                <div className="contact-us">
+                    <h4>Contact</h4>
+                    <p>
+                        <a
+                            href="mailto:conference@scratch.mit.edu"
+                            rel="noopener noreferrer"
+                            target="_blank"
+                        >
+                            Email Us
+                        </a>
+                    </p>
+                </div>
+                <div className="social">
+                    <FlexRow as="ul">
+                        <li>
+                            <a
+                                href="//www.twitter.com/scratch"
+                                rel="noopener noreferrer"
+                                target="_blank"
+                            >
+                                <img
+                                    alt="scratch twitter"
+                                    src="/images/conference/footer/twitter.png"
+                                />
+                            </a>
+                        </li>
+                        <li>
+                            <a
+                                href="//www.facebook.com/scratchteam"
+                                rel="noopener noreferrer"
+                                target="_blank"
+                            >
+                                <img
+                                    alt="scratch facebook"
+                                    src="/images/conference/footer/facebook.png"
+                                />
+                            </a>
+                        </li>
+                        <li>
+                            <a
+                                href="http://medium.com/scratchfoundation-blog"
+                                rel="noopener noreferrer"
+                                target="_blank"
+                            >
+                                <img
+                                    alt="scratch foundation blog"
+                                    src="/images/conference/footer/medium.png"
+                                />
+                            </a>
+                        </li>
+                    </FlexRow>
+                </div>
+            </div>
+        </FlexRow>
+        <LanguageChooser locale={props.intl.locale} />
+    </FooterBox>
+);
+
+ConferenceFooter.propTypes = {
+    intl: intlShape
+};
 
 module.exports = injectIntl(ConferenceFooter);
diff --git a/src/components/footer/container/footer.jsx b/src/components/footer/container/footer.jsx
index 53f7eba59..f629e9f39 100644
--- a/src/components/footer/container/footer.jsx
+++ b/src/components/footer/container/footer.jsx
@@ -1,16 +1,16 @@
-var React = require('react');
+const PropTypes = require('prop-types');
+const React = require('react');
 
 require('./footer.scss');
 
-var FooterBox = React.createClass({
-    type: 'FooterBox',
-    render: function () {
-        return (
-            <div className="inner">
-                {this.props.children}
-            </div>
-        );
-    }
-});
+const FooterBox = props => (
+    <div className="inner">
+        {props.children}
+    </div>
+);
+
+FooterBox.propTypes = {
+    children: PropTypes.node
+};
 
 module.exports = FooterBox;
diff --git a/src/components/footer/www/footer.jsx b/src/components/footer/www/footer.jsx
index 0cfaa46cd..e983b4f8b 100644
--- a/src/components/footer/www/footer.jsx
+++ b/src/components/footer/www/footer.jsx
@@ -1,226 +1,225 @@
-var React = require('react');
-var ReactIntl = require('react-intl');
-var FormattedMessage = ReactIntl.FormattedMessage;
-var injectIntl = ReactIntl.injectIntl;
+const FormattedMessage = require('react-intl').FormattedMessage;
+const injectIntl = require('react-intl').injectIntl;
+const intlShape = require('react-intl').intlShape;
+const MediaQuery = require('react-responsive').default;
+const React = require('react');
 
-var FooterBox = require('../container/footer.jsx');
-var LanguageChooser = require('../../languagechooser/languagechooser.jsx');
+const FooterBox = require('../container/footer.jsx');
+const LanguageChooser = require('../../languagechooser/languagechooser.jsx');
 
-var MediaQuery = require('react-responsive');
-var frameless = require('../../../lib/frameless');
+const frameless = require('../../../lib/frameless');
 
 require('./footer.scss');
 
-var Footer = React.createClass({
-    type: 'Footer',
-    render: function () {
-        return (
-            <FooterBox>
-                <MediaQuery maxWidth={frameless.tablet - 1}>
-                    <div className="lists">
-                        <dl>
-                            <dd>
-                                <a href="/about">
-                                    <FormattedMessage id='general.aboutScratch' />
-                                </a>
-                            </dd>
-                            <dd>
-                                <a href="/jobs">
-                                    <FormattedMessage id='general.jobs' />
-                                </a>
-                            </dd>
-                            <dd>
-                                <a href="/contact-us/">
-                                    <FormattedMessage id='general.contactUs' />
-                                </a>
-                            </dd>
-                        </dl>
-                        <dl>
-                            <dd>
-                                <a href="/terms_of_use">
-                                    <FormattedMessage id='general.termsOfUse' />
-                                </a>
-                            </dd>
-                            <dd>
-                                <a href="/privacy_policy">
-                                    <FormattedMessage id='general.privacyPolicy' />
-                                </a>
-                            </dd>
-                            <dd>
-                                <a href="/community_guidelines">
-                                    <FormattedMessage id='general.guidelines' />
-                                </a>
-                            </dd>
-                        </dl>
-                    </div>
-                </MediaQuery>
-                <MediaQuery minWidth={frameless.tablet}>
-                    <div className="lists">
-                        <dl>
-                            <dt>
-                                <FormattedMessage id='general.about' />
-                            </dt>
-                            <dd>
-                                <a href="/about">
-                                    <FormattedMessage id='general.aboutScratch' />
-                                </a>
-                            </dd>
-                            <dd>
-                                <a href="/parents/">
-                                    <FormattedMessage id='general.forParents' />
-                                </a>
-                            </dd>
-                            <dd>
-                                <a href="/educators">
-                                    <FormattedMessage id='general.forEducators' />
-                                </a>
-                            </dd>
-                            <dd>
-                                <a href="/developers">
-                                    <FormattedMessage id='general.forDevelopers' />
-                                </a>
-                            </dd>
-                            <dd>
-                                <a href="/info/credits">
-                                    <FormattedMessage id='general.credits' />
-                                </a>
-                            </dd>
-                            <dd>
-                                <a href="/jobs">
-                                    <FormattedMessage id='general.jobs' />
-                                </a>
-                            </dd>
-                            <dd>
-                                <a href="http://wiki.scratch.mit.edu/wiki/Scratch_Press">
-                                    <FormattedMessage id='general.press' />
-                                </a>
-                            </dd>
-                        </dl>
-                        <dl>
-                            <dt>
-                                <FormattedMessage id='general.community' />
-                            </dt>
-                            <dd>
-                                <a href="/community_guidelines">
-                                    <FormattedMessage id='general.guidelines' />
-                                </a>
-                            </dd>
-                            <dd>
-                                <a href="/discuss/">
-                                    <FormattedMessage id='footer.discuss' />
-                                </a>
-                            </dd>
-                            <dd>
-                                <a href="https://wiki.scratch.mit.edu/">
-                                    <FormattedMessage id='general.wiki' />
-                                </a>
-                            </dd>
-                            <dd>
-                                <a href="/statistics/">
-                                    <FormattedMessage id='general.statistics' />
-                                </a>
-                            </dd>
-                        </dl>
+const Footer = props => (
+    <FooterBox>
+        <MediaQuery maxWidth={frameless.tablet - 1}>
+            <div className="lists">
+                <dl>
+                    <dd>
+                        <a href="/about">
+                            <FormattedMessage id="general.aboutScratch" />
+                        </a>
+                    </dd>
+                    <dd>
+                        <a href="/jobs">
+                            <FormattedMessage id="general.jobs" />
+                        </a>
+                    </dd>
+                    <dd>
+                        <a href="/contact-us/">
+                            <FormattedMessage id="general.contactUs" />
+                        </a>
+                    </dd>
+                </dl>
+                <dl>
+                    <dd>
+                        <a href="/terms_of_use">
+                            <FormattedMessage id="general.termsOfUse" />
+                        </a>
+                    </dd>
+                    <dd>
+                        <a href="/privacy_policy">
+                            <FormattedMessage id="general.privacyPolicy" />
+                        </a>
+                    </dd>
+                    <dd>
+                        <a href="/community_guidelines">
+                            <FormattedMessage id="general.guidelines" />
+                        </a>
+                    </dd>
+                </dl>
+            </div>
+        </MediaQuery>
+        <MediaQuery minWidth={frameless.tablet}>
+            <div className="lists">
+                <dl>
+                    <dt>
+                        <FormattedMessage id="general.about" />
+                    </dt>
+                    <dd>
+                        <a href="/about">
+                            <FormattedMessage id="general.aboutScratch" />
+                        </a>
+                    </dd>
+                    <dd>
+                        <a href="/parents/">
+                            <FormattedMessage id="general.forParents" />
+                        </a>
+                    </dd>
+                    <dd>
+                        <a href="/educators">
+                            <FormattedMessage id="general.forEducators" />
+                        </a>
+                    </dd>
+                    <dd>
+                        <a href="/developers">
+                            <FormattedMessage id="general.forDevelopers" />
+                        </a>
+                    </dd>
+                    <dd>
+                        <a href="/info/credits">
+                            <FormattedMessage id="general.credits" />
+                        </a>
+                    </dd>
+                    <dd>
+                        <a href="/jobs">
+                            <FormattedMessage id="general.jobs" />
+                        </a>
+                    </dd>
+                    <dd>
+                        <a href="http://wiki.scratch.mit.edu/wiki/Scratch_Press">
+                            <FormattedMessage id="general.press" />
+                        </a>
+                    </dd>
+                </dl>
+                <dl>
+                    <dt>
+                        <FormattedMessage id="general.community" />
+                    </dt>
+                    <dd>
+                        <a href="/community_guidelines">
+                            <FormattedMessage id="general.guidelines" />
+                        </a>
+                    </dd>
+                    <dd>
+                        <a href="/discuss/">
+                            <FormattedMessage id="footer.discuss" />
+                        </a>
+                    </dd>
+                    <dd>
+                        <a href="https://wiki.scratch.mit.edu/">
+                            <FormattedMessage id="general.wiki" />
+                        </a>
+                    </dd>
+                    <dd>
+                        <a href="/statistics/">
+                            <FormattedMessage id="general.statistics" />
+                        </a>
+                    </dd>
+                </dl>
 
-                        <dl>
-                            <dt>
-                                <FormattedMessage id='general.support' />
-                            </dt>
-                            <dd>
-                                <a href="/tips">
-                                    <FormattedMessage id='general.tips' />
-                                </a>
-                            </dd>
-                            <dd>
-                                <a href="/info/faq">
-                                    <FormattedMessage id='general.faq' />
-                                </a>
-                            </dd>
-                            <dd>
-                                <a href="/download">
-                                    <FormattedMessage id='general.offlineEditor' />
-                                </a>
-                            </dd>
-                            <dd>
-                                <a href="/contact-us/">
-                                    <FormattedMessage id='general.contactUs' />
-                                </a>
-                            </dd>
-                            <dd>
-                                <a href="/store">
-                                    <FormattedMessage id='general.scratchStore' />
-                                </a>
-                            </dd>
-                            <dd>
-                                <a href="https://secure.donationpay.org/scratchfoundation/">
-                                    <FormattedMessage id='general.donate'/>
-                                </a>
-                            </dd>
-                        </dl>
+                <dl>
+                    <dt>
+                        <FormattedMessage id="general.support" />
+                    </dt>
+                    <dd>
+                        <a href="/tips">
+                            <FormattedMessage id="general.tips" />
+                        </a>
+                    </dd>
+                    <dd>
+                        <a href="/info/faq">
+                            <FormattedMessage id="general.faq" />
+                        </a>
+                    </dd>
+                    <dd>
+                        <a href="/download">
+                            <FormattedMessage id="general.offlineEditor" />
+                        </a>
+                    </dd>
+                    <dd>
+                        <a href="/contact-us/">
+                            <FormattedMessage id="general.contactUs" />
+                        </a>
+                    </dd>
+                    <dd>
+                        <a href="/store">
+                            <FormattedMessage id="general.scratchStore" />
+                        </a>
+                    </dd>
+                    <dd>
+                        <a href="https://secure.donationpay.org/scratchfoundation/">
+                            <FormattedMessage id="general.donate" />
+                        </a>
+                    </dd>
+                </dl>
 
-                        <dl>
-                            <dt>
-                                <FormattedMessage id='general.legal'/>
-                            </dt>
-                            <dd>
-                                <a href="/terms_of_use">
-                                    <FormattedMessage id='general.termsOfUse' />
-                                </a>
-                            </dd>
-                            <dd>
-                                <a href="/privacy_policy">
-                                    <FormattedMessage id='general.privacyPolicy' />
-                                </a>
-                            </dd>
-                            <dd>
-                                <a href="/DMCA">
-                                    <FormattedMessage id='general.dmca' />
-                                </a>
-                            </dd>
-                        </dl>
+                <dl>
+                    <dt>
+                        <FormattedMessage id="general.legal" />
+                    </dt>
+                    <dd>
+                        <a href="/terms_of_use">
+                            <FormattedMessage id="general.termsOfUse" />
+                        </a>
+                    </dd>
+                    <dd>
+                        <a href="/privacy_policy">
+                            <FormattedMessage id="general.privacyPolicy" />
+                        </a>
+                    </dd>
+                    <dd>
+                        <a href="/DMCA">
+                            <FormattedMessage id="general.dmca" />
+                        </a>
+                    </dd>
+                </dl>
 
-                        <dl>
-                            <dt>
-                                <FormattedMessage id='footer.scratchFamily' />
-                            </dt>
-                            <dd>
-                                <a href="http://scratched.gse.harvard.edu/">
-                                    <FormattedMessage id='general.scratchEd' />
-                                </a>
-                            </dd>
-                            <dd>
-                                <a href="http://www.scratchjr.org/">
-                                    <FormattedMessage id='general.scratchJr' />
-                                </a>
-                            </dd>
-                            <dd>
-                                <a href="http://day.scratch.mit.edu/">
-                                    Scratch Day
-                                </a>
-                            </dd>
-                            <dd>
-                                <a href="/conference">
-                                    <FormattedMessage id='general.scratchConference' />
-                                </a>
-                            </dd>
-                            <dd>
-                                <a href="http://www.scratchfoundation.org/">
-                                    <FormattedMessage id='general.scratchFoundation' />
-                                </a>
-                            </dd>
-                        </dl>
-                    </div>
-                </MediaQuery>
-                <LanguageChooser locale={this.props.intl.locale} />
+                <dl>
+                    <dt>
+                        <FormattedMessage id="footer.scratchFamily" />
+                    </dt>
+                    <dd>
+                        <a href="http://scratched.gse.harvard.edu/">
+                            <FormattedMessage id="general.scratchEd" />
+                        </a>
+                    </dd>
+                    <dd>
+                        <a href="http://www.scratchjr.org/">
+                            <FormattedMessage id="general.scratchJr" />
+                        </a>
+                    </dd>
+                    <dd>
+                        <a href="http://day.scratch.mit.edu/">
+                            Scratch Day
+                        </a>
+                    </dd>
+                    <dd>
+                        <a href="/conference">
+                            <FormattedMessage id="general.scratchConference" />
+                        </a>
+                    </dd>
+                    <dd>
+                        <a href="http://www.scratchfoundation.org/">
+                            <FormattedMessage id="general.scratchFoundation" />
+                        </a>
+                    </dd>
+                </dl>
+            </div>
+        </MediaQuery>
+        <LanguageChooser locale={props.intl.locale} />
 
-                <div className="copyright">
-                    <p>
-                        <FormattedMessage id='general.copyright' />
-                    </p>
-                </div>
-            </FooterBox>
-        );
-    }
-});
+        <div className="copyright">
+            <p>
+                <FormattedMessage id="general.copyright" />
+            </p>
+        </div>
+    </FooterBox>
+);
+
+Footer.propTypes = {
+    intl: intlShape.isRequired
+};
 
 module.exports = injectIntl(Footer);
diff --git a/src/components/forms/button.jsx b/src/components/forms/button.jsx
index 2c12d7db7..b5b97e39a 100644
--- a/src/components/forms/button.jsx
+++ b/src/components/forms/button.jsx
@@ -1,22 +1,26 @@
-var React = require('react');
-var classNames = require('classnames');
+const classNames = require('classnames');
+const omit = require('lodash.omit');
+const PropTypes = require('prop-types');
+const React = require('react');
 
 require('./button.scss');
 
-var Button = React.createClass({
-    type: 'Button',
-    propTypes: {
-        
-    },
-    render: function () {
-        var classes = classNames(
-            'button',
-            this.props.className
-        );
-        return (
-            <button {... this.props} className={classes} >{this.props.children}</button>
-        );
-    }
-});
+const Button = props => {
+    const classes = classNames('button', props.className);
+
+    return (
+        <button
+            className={classes}
+            {...omit(props, ['className', 'children'])}
+        >
+            {props.children}
+        </button>
+    );
+};
+
+Button.propTypes = {
+    children: PropTypes.node,
+    className: PropTypes.string
+};
 
 module.exports = Button;
diff --git a/src/components/forms/charcount.jsx b/src/components/forms/charcount.jsx
index af6192ebe..bf46c95f4 100644
--- a/src/components/forms/charcount.jsx
+++ b/src/components/forms/charcount.jsx
@@ -1,28 +1,28 @@
-var classNames = require('classnames');
-var React = require('react');
+const classNames = require('classnames');
+const PropTypes = require('prop-types');
+const React = require('react');
 
 require('./charcount.scss');
 
-var CharCount = React.createClass({
-    type: 'CharCount',
-    getDefaultProps: function () {
-        return {
-            maxCharacters: 0,
-            currentCharacters: 0
-        };
-    },
-    render: function () {
-        var classes = classNames(
-            'char-count',
-            this.props.className,
-            {overmax: (this.props.currentCharacters > this.props.maxCharacters)}
-        );
-        return (
-            <p className={classes}>
-                {this.props.currentCharacters}/{this.props.maxCharacters}
-            </p>
-        );
-    }
-});
+const CharCount = props => (
+    <p
+        className={classNames('char-count', props.className, {
+            overmax: (props.currentCharacters > props.maxCharacters)
+        })}
+    >
+        {props.currentCharacters}/{props.maxCharacters}
+    </p>
+);
+
+CharCount.propTypes = {
+    className: PropTypes.string,
+    currentCharacters: PropTypes.number,
+    maxCharacters: PropTypes.number
+};
+
+CharCount.defaultProps = {
+    currentCharacters: 0,
+    maxCharacters: 0
+};
 
 module.exports = CharCount;
diff --git a/src/components/forms/checkbox-group.jsx b/src/components/forms/checkbox-group.jsx
index 29321d268..c98a0e5dd 100644
--- a/src/components/forms/checkbox-group.jsx
+++ b/src/components/forms/checkbox-group.jsx
@@ -1,25 +1,25 @@
-var classNames = require('classnames');
-var FRCCheckboxGroup = require('formsy-react-components').CheckboxGroup;
-var React = require('react');
-var defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
-var inputHOC = require('./input-hoc.jsx');
+const classNames = require('classnames');
+const FRCCheckboxGroup = require('formsy-react-components').CheckboxGroup;
+const PropTypes = require('prop-types');
+const React = require('react');
+
+const defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
+const inputHOC = require('./input-hoc.jsx');
 
 require('./row.scss');
 require('./checkbox-group.scss');
 
-var CheckboxGroup = React.createClass({
-    type: 'CheckboxGroup',
-    render: function () {
-        var classes = classNames(
-            'checkbox-group',
-            this.props.className
-        );
-        return (
-            <div className={classes}>
-                <FRCCheckboxGroup {... this.props} className={classes} />
-            </div>
-        );
-    }
-});
+const CheckboxGroup = props => (
+    <div className={classNames('checkbox-group', props.className)}>
+        <FRCCheckboxGroup
+            className={classNames('checkbox-group', props.className)}
+            {... props}
+        />
+    </div>
+);
+
+CheckboxGroup.propTypes = {
+    className: PropTypes.string
+};
 
 module.exports = inputHOC(defaultValidationHOC(CheckboxGroup));
diff --git a/src/components/forms/checkbox.jsx b/src/components/forms/checkbox.jsx
index f0aa295b8..fcaac2293 100644
--- a/src/components/forms/checkbox.jsx
+++ b/src/components/forms/checkbox.jsx
@@ -1,23 +1,23 @@
-var classNames = require('classnames');
-var FRCCheckbox = require('formsy-react-components').Checkbox;
-var React = require('react');
-var defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
-var inputHOC = require('./input-hoc.jsx');
+const classNames = require('classnames');
+const FRCCheckbox = require('formsy-react-components').Checkbox;
+const PropTypes = require('prop-types');
+const React = require('react');
+
+const defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
+const inputHOC = require('./input-hoc.jsx');
 
 require('./row.scss');
 require('./checkbox.scss');
 
-var Checkbox = React.createClass({
-    type: 'Checkbox',
-    render: function () {
-        var classes = classNames(
-            'checkbox-row',
-            this.props.className
-        );
-        return (
-            <FRCCheckbox rowClassName={classes} {... this.props} />
-        );
-    }
-});
+const Checkbox = props => (
+    <FRCCheckbox
+        rowClassName={classNames('checkbox-row', props.className)}
+        {...props}
+    />
+);
+
+Checkbox.propTypes = {
+    className: PropTypes.string
+};
 
 module.exports = inputHOC(defaultValidationHOC(Checkbox));
diff --git a/src/components/forms/form.jsx b/src/components/forms/form.jsx
index 17ada223a..1514787b7 100644
--- a/src/components/forms/form.jsx
+++ b/src/components/forms/form.jsx
@@ -1,47 +1,61 @@
-var classNames = require('classnames');
-var Formsy = require('formsy-react');
-var omit = require('lodash.omit');
-var React = require('react');
-var validations = require('./validations.jsx').validations;
+const bindAll = require('lodash.bindall');
+const classNames = require('classnames');
+const Formsy = require('formsy-react');
+const omit = require('lodash.omit');
+const PropTypes = require('prop-types');
+const React = require('react');
 
-for (var validation in validations) {
+const validations = require('./validations.jsx').validations;
+
+for (const validation in validations) {
     Formsy.addValidationRule(validation, validations[validation]);
 }
 
-var Form = React.createClass({
-    getDefaultProps: function () {
-        return {
-            noValidate: true,
-            onChange: function () {}
-        };
-    },
-    getInitialState: function () {
-        return {
+class Form extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleChange'
+        ]);
+        this.state = {
             allValues: {}
         };
-    },
-    onChange: function (currentValues, isChanged) {
+    }
+    handleChange (currentValues, isChanged) {
         this.setState({allValues: omit(currentValues, 'all')});
         this.props.onChange(currentValues, isChanged);
-    },
-    render: function () {
-        var classes = classNames(
-            'form',
-            this.props.className
-        );
+    }
+    render () {
         return (
-            <Formsy.Form {... this.props} className={classes} ref="formsy" onChange={this.onChange}>
-                {React.Children.map(this.props.children, function (child) {
+            <Formsy.Form
+                className={classNames('form', this.props.className)}
+                ref={form => {
+                    this.formsy = form;
+                }}
+                onChange={this.handleChange}
+                {...this.props}
+            >
+                {React.Children.map(this.props.children, child => {
                     if (!child) return child;
                     if (child.props.name === 'all') {
                         return React.cloneElement(child, {value: this.state.allValues});
-                    } else {
-                        return child;
                     }
-                }.bind(this))}
+                    return child;
+                })}
             </Formsy.Form>
         );
     }
-});
+}
+
+Form.propTypes = {
+    children: PropTypes.node,
+    className: PropTypes.string,
+    onChange: PropTypes.func
+};
+
+Form.defaultProps = {
+    noValidate: true,
+    onChange: function () {}
+};
 
 module.exports = Form;
diff --git a/src/components/forms/general-error.jsx b/src/components/forms/general-error.jsx
index d9e20463b..270e3ca39 100644
--- a/src/components/forms/general-error.jsx
+++ b/src/components/forms/general-error.jsx
@@ -1,5 +1,6 @@
-var Formsy = require('formsy-react');
-var React = require('react');
+const Formsy = require('formsy-react');
+const PropTypes = require('prop-types');
+const React = require('react');
 
 require('./general-error.scss');
 
@@ -10,13 +11,18 @@ require('./general-error.scss');
  * give it a name, and apply your validation error to
  * the name of the GeneralError component.
  */
-module.exports = Formsy.HOC(React.createClass({
-    render: function () {
-        if (!this.props.showError()) return null;
-        return (
-            <p className="general-error">
-                {this.props.getErrorMessage()}
-            </p>
-        );
-    }
-}));
+const GeneralError = props => {
+    if (!props.showError()) return null;
+    return (
+        <p className="general-error">
+            {props.getErrorMessage()}
+        </p>
+    );
+};
+
+GeneralError.propTypes = {
+    getErrorMessage: PropTypes.func,
+    showError: PropTypes.func
+};
+
+module.exports = Formsy.HOC(GeneralError);
diff --git a/src/components/forms/input-hoc.jsx b/src/components/forms/input-hoc.jsx
index 1532eaefd..1ce3c1c0b 100644
--- a/src/components/forms/input-hoc.jsx
+++ b/src/components/forms/input-hoc.jsx
@@ -1,20 +1,32 @@
-var React = require('react');
+const omit = require('lodash.omit');
+const PropTypes = require('prop-types');
+const React = require('react');
 
-module.exports = function InputComponentMixin (Component) {
-    var InputComponent = React.createClass({
-        getDefaultProps: function () {
-            return {
-                messages: {
-                    'general.notRequired': 'Not Required'
-                }
-            };
-        },
-        render: function () {
-            return (
-                <Component help={this.props.required ? null : this.props.messages['general.notRequired']}
-                           {...this.props} />
-            );
+/**
+ * Higher-order component for building an input field
+ * @param  {React.Component} Component an input component
+ * @return {React.Component}           a wrapped input component
+ */
+module.exports = Component => {
+    const InputComponent = props => (
+        <Component
+            help={props.required ? null : props.messages['general.notRequired']}
+            {...omit(props, ['messages'])}
+        />
+    );
+
+    InputComponent.propTypes = {
+        messages: PropTypes.shape({
+            'general.notRequired': PropTypes.string
+        }),
+        required: PropTypes.oneOfType([PropTypes.bool, PropTypes.string])
+    };
+
+    InputComponent.defaultProps = {
+        messages: {
+            'general.notRequired': 'Not Required'
         }
-    });
+    };
+
     return InputComponent;
 };
diff --git a/src/components/forms/input.jsx b/src/components/forms/input.jsx
index 3244ade3c..a9992a40c 100644
--- a/src/components/forms/input.jsx
+++ b/src/components/forms/input.jsx
@@ -1,46 +1,57 @@
-var classNames = require('classnames');
-var FRCInput = require('formsy-react-components').Input;
-var React = require('react');
-var defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
-var inputHOC = require('./input-hoc.jsx');
+const bindAll = require('lodash.bindall');
+const classNames = require('classnames');
+const FRCInput = require('formsy-react-components').Input;
+const omit = require('lodash.omit');
+const PropTypes = require('prop-types');
+const React = require('react');
+
+const defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
+const inputHOC = require('./input-hoc.jsx');
 
 require('./input.scss');
 require('./row.scss');
 
-var Input = React.createClass({
-    type: 'Input',
-    getDefaultProps: function () {
-        return {};
-    },
-    getInitialState: function () {
-        return {
+class Input extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleInvalid',
+            'handleValid'
+        ]);
+        this.state = {
             status: ''
         };
-    },
-    onValid: function () {
+    }
+    handleValid () {
         this.setState({
             status: 'pass'
         });
-    },
-    onInvalid: function () {
+    }
+    handleInvalid () {
         this.setState({
             status: 'fail'
         });
-    },
-    render: function () {
-        var classes = classNames(
-            this.state.status,
-            this.props.className,
-            {'no-label': (typeof this.props.label === 'undefined')}
-        );
+    }
+    render () {
         return (
-            <FRCInput {... this.props}
-                      className="input"
-                      rowClassName={classes}
-                      onValid={this.onValid}
-                      onInvalid={this.onInvalid} />
+            <FRCInput
+                className="input"
+                rowClassName={classNames(
+                    this.state.status,
+                    this.props.className,
+                    {'no-label': (typeof this.props.label === 'undefined')}
+                )}
+                onInvalid={this.handleInvalid}
+                onValid={this.handleValid}
+                {...omit(this.props, ['className'])}
+            />
         );
     }
-});
+}
+
+Input.propTypes = {
+    className: PropTypes.string,
+    label: PropTypes.string
+};
 
 module.exports = inputHOC(defaultValidationHOC(Input));
diff --git a/src/components/forms/phone-input.jsx b/src/components/forms/phone-input.jsx
index 260c5a2ef..69d8ab23f 100644
--- a/src/components/forms/phone-input.jsx
+++ b/src/components/forms/phone-input.jsx
@@ -1,23 +1,33 @@
-var allCountries = require('react-telephone-input/lib/country_data').allCountries;
-var classNames = require('classnames');
-var ComponentMixin = require('formsy-react-components').ComponentMixin;
-var FormsyMixin = require('formsy-react').Mixin;
-var React = require('react');
-var ReactPhoneInput = require('react-telephone-input/lib/withStyles');
-var Row = require('formsy-react-components').Row;
+const allCountries = require('react-telephone-input/lib/country_data').allCountries;
+const classNames = require('classnames');
+const ComponentMixin = require('formsy-react-components').ComponentMixin;
+const createReactClass = require('create-react-class');
+const FormsyMixin = require('formsy-react').Mixin;
+const omit = require('lodash.omit');
+const PropTypes = require('prop-types');
+const React = require('react');
+const ReactPhoneInput = require('react-telephone-input/lib/withStyles').default;
+const Row = require('formsy-react-components').Row;
 
-var defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
-var inputHOC = require('./input-hoc.jsx');
-var intl = require('../../lib/intl.jsx');
-var validationHOCFactory = require('./validations.jsx').validationHOCFactory;
+const defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
+const inputHOC = require('./input-hoc.jsx');
+const intl = require('../../lib/intl.jsx');
+const validationHOCFactory = require('./validations.jsx').validationHOCFactory;
 
-var allIso2 = allCountries.map(function (country) {return country.iso2;});
+const allIso2 = allCountries.map(country => (country.iso2));
 
 require('./row.scss');
 require('./phone-input.scss');
 
-var PhoneInput = React.createClass({
+const PhoneInput = createReactClass({ // eslint-disable-line react/prefer-es6-class
     displayName: 'PhoneInput',
+    propTypes: {
+        className: PropTypes.string,
+        defaultCountry: PropTypes.string,
+        disabled: PropTypes.bool,
+        name: PropTypes.string,
+        onChange: PropTypes.func
+    },
     mixins: [
         FormsyMixin,
         ComponentMixin
@@ -31,29 +41,34 @@ var PhoneInput = React.createClass({
             defaultCountry: 'us'
         };
     },
-    onChangeInput: function (number, country) {
-        var value = {national_number: number, country_code: country};
+    handleChangeInput: function (number, country) {
+        const value = {
+            national_number: number,
+            country_code: country
+        };
         this.setValue(value);
         this.props.onChange(this.props.name, value);
     },
     render: function () {
-        var defaultCountry = PhoneInput.getDefaultProps().defaultCountry;
+        let defaultCountry = PhoneInput.getDefaultProps().defaultCountry;
         if (allIso2.indexOf(this.props.defaultCountry.toLowerCase()) !== -1) {
-            defaultCountry =  this.props.defaultCountry.toLowerCase();
+            defaultCountry = this.props.defaultCountry.toLowerCase();
         }
         return (
-            <Row {... this.getRowProperties()}
-                 htmlFor={this.getId()}
-                 rowClassName={classNames('phone-input', this.props.className)}
+            <Row
+                htmlFor={this.getId()}
+                rowClassName={classNames('phone-input', this.props.className)}
+                {...this.getRowProperties()}
             >
                 <div className="input-group">
-                    <ReactPhoneInput className="form-control"
-                                     {... this.props}
-                                     defaultCountry={defaultCountry}
-                                     onChange={this.onChangeInput}
-                                     id={this.getId()}
-                                     label={null}
-                                     disabled={this.isFormDisabled() || this.props.disabled}
+                    <ReactPhoneInput
+                        className="form-control"
+                        defaultCountry={defaultCountry}
+                        disabled={this.isFormDisabled() || this.props.disabled}
+                        id={this.getId()}
+                        label={null}
+                        onChange={this.handleChangeInput}
+                        {...omit(this.props, ['className', 'disabled', 'onChange'])}
                     />
                     {this.renderHelp()}
                     {this.renderErrorMessage()}
@@ -63,7 +78,7 @@ var PhoneInput = React.createClass({
     }
 });
 
-var phoneValidationHOC = validationHOCFactory({
+const phoneValidationHOC = validationHOCFactory({
     isPhone: <intl.FormattedMessage id="teacherRegistration.validationPhoneNumber" />
 });
 
diff --git a/src/components/forms/radio-group.jsx b/src/components/forms/radio-group.jsx
index fcdf928a6..168f0a926 100644
--- a/src/components/forms/radio-group.jsx
+++ b/src/components/forms/radio-group.jsx
@@ -1,23 +1,23 @@
-var classNames = require('classnames');
-var FRCRadioGroup = require('formsy-react-components').RadioGroup;
-var React = require('react');
-var defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
-var inputHOC = require('./input-hoc.jsx');
+const classNames = require('classnames');
+const FRCRadioGroup = require('formsy-react-components').RadioGroup;
+const PropTypes = require('prop-types');
+const React = require('react');
+
+const defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
+const inputHOC = require('./input-hoc.jsx');
 
 require('./row.scss');
 require('./radio-group.scss');
 
-var RadioGroup = React.createClass({
-    type: 'RadioGroup',
-    render: function () {
-        var classes = classNames(
-            'radio-group',
-            this.props.className
-        );
-        return (
-            <FRCRadioGroup {... this.props} className={classes} />
-        );
-    }
-});
+const RadioGroup = props => (
+    <FRCRadioGroup
+        className={classNames('radio-group', props.className)}
+        {... props}
+    />
+);
+
+RadioGroup.propTypes = {
+    className: PropTypes.string
+};
 
 module.exports = inputHOC(defaultValidationHOC(RadioGroup));
diff --git a/src/components/forms/select.jsx b/src/components/forms/select.jsx
index a17053942..16a7e1ee2 100644
--- a/src/components/forms/select.jsx
+++ b/src/components/forms/select.jsx
@@ -1,33 +1,31 @@
-var classNames = require('classnames');
-var defaults = require('lodash.defaultsdeep');
-var FRCSelect = require('formsy-react-components').Select;
-var React = require('react');
-var defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
-var inputHOC = require('./input-hoc.jsx');
+const classNames = require('classnames');
+const defaults = require('lodash.defaultsdeep');
+const FRCSelect = require('formsy-react-components').Select;
+const PropTypes = require('prop-types');
+const React = require('react');
+
+const defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
+const inputHOC = require('./input-hoc.jsx');
 
 require('./row.scss');
 require('./select.scss');
 
-var Select = React.createClass({
-    type: 'Select',
-    propTypes: {
-        
-    },
-    render: function () {
-        var classes = classNames(
-            'select',
-            this.props.className
-        );
-        var props = this.props;
-        if (this.props.required && !this.props.value) {
-            props = defaults({}, this.props, {value: this.props.options[0].value});
-        }
-        return (
-            <div className={classes}>
-                <FRCSelect {... props} />
-            </div>
-        );
+const Select = props => {
+    if (props.required && !props.value) {
+        props = defaults({}, props, {value: props.options[0].value});
     }
-});
+    return (
+        <div className={classNames('select', props.className)}>
+            <FRCSelect {...props} />
+        </div>
+    );
+};
+
+Select.propTypes = {
+    className: PropTypes.string,
+    options: PropTypes.arrayOf(PropTypes.any),
+    required: PropTypes.bool,
+    value: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
+};
 
 module.exports = inputHOC(defaultValidationHOC(Select));
diff --git a/src/components/forms/textarea.jsx b/src/components/forms/textarea.jsx
index ec90f8b59..0c488f995 100644
--- a/src/components/forms/textarea.jsx
+++ b/src/components/forms/textarea.jsx
@@ -1,25 +1,25 @@
-var classNames = require('classnames');
-var FRCTextarea = require('formsy-react-components').Textarea;
-var React = require('react');
-var defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
-var inputHOC = require('./input-hoc.jsx');
+const classNames = require('classnames');
+const FRCTextarea = require('formsy-react-components').Textarea;
+const omit = require('lodash.omit');
+const PropTypes = require('prop-types');
+const React = require('react');
+
+const defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
+const inputHOC = require('./input-hoc.jsx');
 
 require('./row.scss');
 require('./textarea.scss');
 
-var TextArea = React.createClass({
-    type: 'TextArea',
-    render: function () {
-        var classes = classNames(
-            'textarea-row',
-            this.props.className
-        );
-        return (
-            <FRCTextarea {... this.props}
-                         className="textarea"
-                         rowClassName={classes} />
-        );
-    }
-});
+const TextArea = props => (
+    <FRCTextarea
+        className="textarea"
+        rowClassName={classNames('textarea-row', props.className)}
+        {...omit(props, ['className'])}
+    />
+);
+
+TextArea.propTypes = {
+    className: PropTypes.string
+};
 
 module.exports = inputHOC(defaultValidationHOC(TextArea));
diff --git a/src/components/forms/validations.jsx b/src/components/forms/validations.jsx
index 113cd6fb4..c83ce63cb 100644
--- a/src/components/forms/validations.jsx
+++ b/src/components/forms/validations.jsx
@@ -1,48 +1,46 @@
-var defaults = require('lodash.defaultsdeep');
-var intl = require('../../lib/intl.jsx');
-var libphonenumber = require('google-libphonenumber');
-var phoneNumberUtil = libphonenumber.PhoneNumberUtil.getInstance();
-var React = require('react');
-
-module.exports = {};
+const defaults = require('lodash.defaultsdeep');
+const intl = require('../../lib/intl.jsx');
+const libphonenumber = require('google-libphonenumber');
+const omit = require('lodash.omit');
+const phoneNumberUtil = libphonenumber.PhoneNumberUtil.getInstance();
+const PropTypes = require('prop-types');
+const React = require('react');
 
 module.exports.validations = {
-    notEquals: function (values, value, neq) {
-        return value !== neq;
-    },
-    notEqualsField: function (values, value, field) {
-        return value !== values[field];
-    },
-    isPhone: function (values, value) {
+    notEquals: (values, value, neq) => (value !== neq),
+    notEqualsField: (values, value, field) => (value !== values[field]),
+    isPhone: (values, value) => {
         if (typeof value === 'undefined') return true;
         if (value && value.national_number === '+') return true;
         try {
-            var parsed = phoneNumberUtil.parse(value.national_number, value.country_code.iso2);
+            const parsed = phoneNumberUtil.parse(value.national_number, value.country_code.iso2);
+            return phoneNumberUtil.isValidNumber(parsed);
         } catch (err) {
             return false;
         }
-        return phoneNumberUtil.isValidNumber(parsed);
     }
 };
+
 module.exports.validations.notEqualsUsername = module.exports.validations.notEquals;
 
-module.exports.validationHOCFactory = function (defaultValidationErrors) {
-    return function (Component) {
-        var ValidatedComponent = React.createClass({
-            render: function () {
-                var validationErrors = defaults(
-                    {},
-                    defaultValidationErrors,
-                    this.props.validationErrors
-                );
-                return (
-                    <Component {...this.props} validationErrors={validationErrors} />
-                );
-            }
-        });
-        return ValidatedComponent;
+module.exports.validationHOCFactory = defaultValidationErrors => (Component => {
+    const ValidatedComponent = props => (
+        <Component
+            validationErrors={defaults(
+                {},
+                defaultValidationErrors,
+                props.validationErrors
+            )}
+            {...omit(props, ['validationErrors'])}
+        />
+    );
+
+    ValidatedComponent.propTypes = {
+        validationErrors: PropTypes.object // eslint-disable-line react/forbid-prop-types
     };
-};
+
+    return ValidatedComponent;
+});
 
 module.exports.defaultValidationHOC = module.exports.validationHOCFactory({
     isDefaultRequiredValue: <intl.FormattedMessage id="form.validationRequired" />
diff --git a/src/components/grid/grid.jsx b/src/components/grid/grid.jsx
index f130e5bbd..0bc8c9286 100644
--- a/src/components/grid/grid.jsx
+++ b/src/components/grid/grid.jsx
@@ -1,79 +1,68 @@
-var classNames = require('classnames');
-var React = require('react');
+const classNames = require('classnames');
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var Thumbnail = require('../thumbnail/thumbnail.jsx');
-var FlexRow = require('../flex-row/flex-row.jsx');
+const Thumbnail = require('../thumbnail/thumbnail.jsx');
+const FlexRow = require('../flex-row/flex-row.jsx');
 
 require('./grid.scss');
 
-var Grid = React.createClass({
-    type: 'Grid',
-    getDefaultProps: function () {
-        return {
-            items: require('./grid.json'),
-            itemType: 'projects',
-            showLoves: false,
-            showFavorites: false,
-            showRemixes: false,
-            showViews: false,
-            showAvatar: false
-        };
-    },
-    render: function () {
-        var classes = classNames(
-            'grid',
-            this.props.className
-        );
-        return (
-            <div className={classes}>
-                <FlexRow>
-                    {this.props.items.map(function (item, key) {
-                        var href = '/' + this.props.itemType + '/' + item.id + '/';
+const Grid = props => (
+    <div className={classNames('grid', props.className)}>
+        <FlexRow>
+            {props.items.map((item, key) => {
+                const href = `/${props.itemType}/${item.id}/`;
+                if (props.itemType === 'projects') {
+                    return (
+                        <Thumbnail
+                            avatar={`https://cdn2.scratch.mit.edu/get_image/user/${item.author.id}_32x32.png`}
+                            creator={item.author.username}
+                            favorites={item.stats.favorites}
+                            href={href}
+                            key={key}
+                            loves={item.stats.loves}
+                            remixes={item.stats.remixes}
+                            showAvatar={props.showAvatar}
+                            showFavorites={props.showFavorites}
+                            showLoves={props.showLoves}
+                            showRemixes={props.showRemixes}
+                            showViews={props.showViews}
+                            src={item.image}
+                            title={item.title}
+                            type={'project'}
+                            views={item.stats.views}
+                        />
+                    );
+                }
+                return (
+                    <Thumbnail
+                        href={href}
+                        key={key}
+                        owner={item.owner}
+                        src={item.image}
+                        title={item.title}
+                        type={'gallery'}
+                    />
+                );
+            })}
+        </FlexRow>
+    </div>
+);
 
-                        if (this.props.itemType == 'projects') {
-                            return (
-                                <Thumbnail
-                                    key={key}
-                                    showLoves={this.props.showLoves}
-                                    showFavorites={this.props.showFavorites}
-                                    showRemixes={this.props.showRemixes}
-                                    showViews={this.props.showViews}
-                                    showAvatar={this.props.showAvatar}
-                                    type={'project'}
-                                    href={href}
-                                    title={item.title}
-                                    src={item.image}
-                                    avatar={
-                                       'https://uploads.scratch.mit.edu/users/avatars/' +
-                                       item.author.id +
-                                       '.png'
-                                    }
-                                    creator={item.author.username}
-                                    loves={item.stats.loves}
-                                    favorites={item.stats.favorites}
-                                    remixes={item.stats.remixes}
-                                    views={item.stats.views}
-                                />
-                            );
-                        }
-                        else {
-                            return (
-                                <Thumbnail
-                                    key={key}
-                                    type={'gallery'}
-                                    href={href}
-                                    title={item.title}
-                                    src={item.image}
-                                    srcDefault={'https://uploads.scratch.mit.edu/galleries/thumbnails/default.png'}
-                                    owner={item.owner}
-                                />
-                            );
-                        }
-                    }.bind(this))}
-                </FlexRow>
-            </div>
-        );
-    }
-});
+Grid.propTypes = {
+    className: PropTypes.string,
+    itemType: PropTypes.string,
+    items: PropTypes.arrayOf(PropTypes.object)
+};
+
+Grid.defaultProps = {
+    items: require('./grid.json'),
+    itemType: 'projects',
+    showLoves: false,
+    showFavorites: false,
+    showRemixes: false,
+    showViews: false,
+    showAvatar: false
+};
 
 module.exports = Grid;
diff --git a/src/components/informationpage/informationpage.jsx b/src/components/informationpage/informationpage.jsx
index acc68da77..94a8bd740 100644
--- a/src/components/informationpage/informationpage.jsx
+++ b/src/components/informationpage/informationpage.jsx
@@ -1,39 +1,34 @@
-var classNames = require('classnames');
-var React = require('react');
-var TitleBanner = require('../../components/title-banner/title-banner.jsx');
+const classNames = require('classnames');
+const PropTypes = require('prop-types');
+const React = require('react');
+
+const TitleBanner = require('../../components/title-banner/title-banner.jsx');
 
 require('./informationpage.scss');
 
-/**
+/*
  * Container for a table of contents
  * alongside a long body of text
  */
-var InformationPage = React.createClass({
-    type: 'InformationPage',
-    propTypes: {
-        title: React.PropTypes.string.isRequired
-    },
-    render: function () {
-        var classes = classNames(
-            'info-outer',
-            'inner',
-            this.props.className
-        );
-        return (
-            <div className="information-page">
-                <TitleBanner className="masthead">
-                    <div className="inner">
-                        <h1 className="title-banner-h1">
-                            {this.props.title}
-                        </h1>
-                    </div>
-                </TitleBanner>
-                <div className={classes}>
-                    {this.props.children}
-                </div>
+const InformationPage = props => (
+    <div className="information-page">
+        <TitleBanner className="masthead">
+            <div className="inner">
+                <h1 className="title-banner-h1">
+                    {props.title}
+                </h1>
             </div>
-        );
-    }
-});
+        </TitleBanner>
+        <div className={classNames('info-outer', 'inner', props.className)}>
+            {props.children}
+        </div>
+    </div>
+);
+
+InformationPage.propTypes = {
+    children: PropTypes.node,
+    className: PropTypes.string,
+    title: PropTypes.string.isRequired
+};
 
 module.exports = InformationPage;
diff --git a/src/components/intro/intro.jsx b/src/components/intro/intro.jsx
index d1f7c4275..efe0473a7 100644
--- a/src/components/intro/intro.jsx
+++ b/src/components/intro/intro.jsx
@@ -1,111 +1,128 @@
-var connect = require('react-redux').connect;
-var React = require('react');
+const bindAll = require('lodash.bindall');
+const connect = require('react-redux').connect;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var sessionActions = require('../../redux/session.js');
+const sessionActions = require('../../redux/session.js');
 
-var IframeModal = require('../modal/iframe/modal.jsx');
-var Registration = require('../registration/registration.jsx');
+const IframeModal = require('../modal/iframe/modal.jsx');
+const Registration = require('../registration/registration.jsx');
 
 require('./intro.scss');
 
-var Intro = React.createClass({
-    type: 'Intro',
-    getDefaultProps: function () {
-        return {
-            messages: {
-                'intro.aboutScratch': 'ABOUT SCRATCH',
-                'intro.forEducators': 'FOR EDUCATORS',
-                'intro.forParents': 'FOR PARENTS',
-                'intro.itsFree': 'it\'s free!',
-                'intro.joinScratch': 'JOIN SCRATCH',
-                'intro.seeExamples': 'SEE EXAMPLES',
-                'intro.tagLine': 'Create stories, games, and animations<br /> Share with others around the world',
-                'intro.tryItOut': 'TRY IT OUT',
-                'intro.description': 'A creative learning community with <span class="project-count"> ' +
-                                     'over 14 million </span>projects shared'
-            },
-            session: {}
-        };
-    },
-    getInitialState: function () {
-        return {
+class Intro extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleShowVideo',
+            'handleCloseVideo',
+            'handleJoinClick',
+            'handleCloseRegistration',
+            'handleCompleteRegistration'
+        ]);
+        this.state = {
             videoOpen: false
         };
-    },
-    showVideo: function () {
+    }
+    handleShowVideo () {
         this.setState({videoOpen: true});
-    },
-    closeVideo: function () {
+    }
+    handleCloseVideo () {
         this.setState({videoOpen: false});
-    },
-    handleJoinClick: function (e) {
+    }
+    handleJoinClick (e) {
         e.preventDefault();
-        this.setState({'registrationOpen': true});
-    },
-    closeRegistration: function () {
-        this.setState({'registrationOpen': false});
-    },
-    completeRegistration: function () {
+        this.setState({registrationOpen: true});
+    }
+    handleCloseRegistration () {
+        this.setState({registrationOpen: false});
+    }
+    handleCompleteRegistration () {
         this.props.dispatch(sessionActions.refreshSession());
         this.closeRegistration();
-    },
-    render: function () {
+    }
+    render () {
         return (
             <div className="intro">
                 <div className="content">
-                    <h1 dangerouslySetInnerHTML={{__html: this.props.messages['intro.tagLine']}}>
-                    </h1>
+                    <h1
+                        dangerouslySetInnerHTML={{ // eslint-disable-line react/no-danger
+                            __html: this.props.messages['intro.tagLine']
+                        }}
+                    />
                     <div className="sprites">
-                        <a className="sprite sprite-1" href="/projects/editor/?tip_bar=getStarted">
+                        <a
+                            className="sprite sprite-1"
+                            href="/projects/editor/?tip_bar=getStarted"
+                        >
                             <img
+                                alt="Scratch Cat"
                                 className="costume costume-1"
                                 src="//cdn.scratch.mit.edu/scratchr2/static/images/cat-a.png"
-                                alt="Scratch Cat" />
+                            />
                             <img
+                                alt="Scratch Cat"
                                 className="costume costume-2"
                                 src="//cdn.scratch.mit.edu/scratchr2/static/images/cat-b.png"
-                                alt="Scratch Cat" />
-                            <div className="circle"></div>
+                            />
+                            <div className="circle" />
                             <div className="text">
                                 {this.props.messages['intro.tryItOut']}
                             </div>
                         </a>
-                        <a className="sprite sprite-2" href="/starter_projects/">
+                        <a
+                            className="sprite sprite-2"
+                            href="/starter_projects/"
+                        >
                             <img
+                                alt="Tera"
                                 className="costume costume-1"
                                 src="//cdn.scratch.mit.edu/scratchr2/static/images/tera-a.png"
-                                alt="Tera" />
+                            />
                             <img
+                                alt="Tera"
                                 className="costume costume-2"
                                 src="//cdn.scratch.mit.edu/scratchr2/static/images/tera-b.png"
-                                alt="Tera" />
-                            <div className="circle"></div>
+                            />
+                            <div className="circle" />
                             <div className="text">
                                 {this.props.messages['intro.seeExamples']}
                             </div>
                         </a>
-                        <a className="sprite sprite-3" href="#" onClick={this.handleJoinClick}>
+                        <a
+                            className="sprite sprite-3"
+                            href="#"
+                            onClick={this.handleJoinClick}
+                        >
                             <img
+                                alt="Gobo"
                                 className="costume costume-1"
                                 src="//cdn.scratch.mit.edu/scratchr2/static/images/gobo-a.png"
-                                alt="Gobo" />
+                            />
                             <img
+                                alt="Gobo"
                                 className="costume costume-2"
                                 src="//cdn.scratch.mit.edu/scratchr2/static/images/gobo-b.png"
-                                alt="Gobo" />
-                            <div className="circle"></div>
+                            />
+                            <div className="circle" />
                             <div className="text">
                                 {this.props.messages['intro.joinScratch']}
                             </div>
                             <div className="text subtext">{this.props.messages['intro.itsFree']}</div>
                         </a>
-                        <Registration key="registration"
-                                      isOpen={this.state.registrationOpen}
-                                      onRequestClose={this.closeRegistration}
-                                      onRegistrationDone={this.completeRegistration} />
+                        <Registration
+                            isOpen={this.state.registrationOpen}
+                            key="registration"
+                            onRegistrationDone={this.handleCompleteRegistration}
+                            onRequestClose={this.handleCloseRegistration}
+                        />
                     </div>
-                    <div className="description"
-                         dangerouslySetInnerHTML={{__html: this.props.messages['intro.description']}}></div>
+                    <div
+                        className="description"
+                        dangerouslySetInnerHTML={{ // eslint-disable-line react/no-danger
+                            __html: this.props.messages['intro.description']
+                        }}
+                    />
                     <div className="links">
                         <a href="/about/">
                             {this.props.messages['intro.aboutScratch']}
@@ -113,33 +130,70 @@ var Intro = React.createClass({
                         <a href="/educators/">
                             {this.props.messages['intro.forEducators']}
                         </a>
-                        <a className="last" href="/parents/">
+                        <a
+                            className="last"
+                            href="/parents/"
+                        >
                             {this.props.messages['intro.forParents']}
                         </a>
                     </div>
                 </div>
                 <div className="video">
-                    <div className="play-button" onClick={this.showVideo}></div>
-                    <img src="//cdn.scratch.mit.edu/scratchr2/static/images/hp-video-screenshot.png"
-                         alt="Intro Video" />
+                    <div
+                        className="play-button"
+                        onClick={this.handleShowVideo}
+                    />
+                    <img
+                        alt="Intro Video"
+                        src="//cdn.scratch.mit.edu/scratchr2/static/images/hp-video-screenshot.png"
+                    />
                 </div>
                 <IframeModal
                     className="mod-intro-video"
                     isOpen={this.state.videoOpen}
-                    onRequestClose={this.closeVideo}
                     src="//player.vimeo.com/video/65583694?title=0&amp;byline=0&amp;portrait=0"
+                    onRequestClose={this.handleCloseVideo}
                 />
             </div>
         );
     }
-});
+}
 
-var mapStateToProps = function (state) {
-    return {
-        session: state.session
-    };
+Intro.propTypes = {
+    dispatch: PropTypes.func.isRequired,
+    messages: PropTypes.shape({
+        'intro.aboutScratch': PropTypes.string,
+        'intro.forEducators': PropTypes.string,
+        'intro.forParents': PropTypes.string,
+        'intro.itsFree': PropTypes.string,
+        'intro.joinScratch': PropTypes.string,
+        'intro.seeExamples': PropTypes.string,
+        'intro.tagLine': PropTypes.string,
+        'intro.tryItOut': PropTypes.string,
+        'intro.description': PropTypes.string
+    })
 };
 
-var ConnectedIntro = connect(mapStateToProps)(Intro);
+Intro.defaultProps = {
+    messages: {
+        'intro.aboutScratch': 'ABOUT SCRATCH',
+        'intro.forEducators': 'FOR EDUCATORS',
+        'intro.forParents': 'FOR PARENTS',
+        'intro.itsFree': 'it\'s free!',
+        'intro.joinScratch': 'JOIN SCRATCH',
+        'intro.seeExamples': 'SEE EXAMPLES',
+        'intro.tagLine': 'Create stories, games, and animations<br /> Share with others around the world',
+        'intro.tryItOut': 'TRY IT OUT',
+        'intro.description': 'A creative learning community with <span class="project-count"> ' +
+                             'over 14 million </span>projects shared'
+    },
+    session: {}
+};
+
+const mapStateToProps = state => ({
+    session: state.session
+});
+
+const ConnectedIntro = connect(mapStateToProps)(Intro);
 
 module.exports = ConnectedIntro;
diff --git a/src/components/languagechooser/languagechooser.jsx b/src/components/languagechooser/languagechooser.jsx
index c538fb923..1e9258853 100644
--- a/src/components/languagechooser/languagechooser.jsx
+++ b/src/components/languagechooser/languagechooser.jsx
@@ -1,46 +1,57 @@
-var classNames = require('classnames');
-var React = require('react');
+const bindAll = require('lodash.bindall');
+const classNames = require('classnames');
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var jar  = require('../../lib/jar.js');
-var languages = require('../../../languages.json');
-var Form = require('../forms/form.jsx');
-var Select = require('../forms/select.jsx');
+const jar = require('../../lib/jar.js');
+const languages = require('../../../languages.json');
+const Form = require('../forms/form.jsx');
+const Select = require('../forms/select.jsx');
 
 require('./languagechooser.scss');
 
 /**
  * Footer dropdown menu that allows one to change their language.
  */
-var LanguageChooser = React.createClass({
-    type: 'LanguageChooser',
-    getDefaultProps: function () {
-        return {
-            languages: languages,
-            locale: 'en'
-        };
-    },
-    onSetLanguage: function (name, value) {
+class LanguageChooser extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleSetLanguage'
+        ]);
+    }
+    handleSetLanguage (name, value) {
         jar.set('scratchlanguage', value);
         window.location.reload();
-    },
-    render: function () {
-        var classes = classNames(
-            'language-chooser',
-            this.props.className
-        );
-        var languageOptions = Object.keys(this.props.languages).map(function (value) {
-            return {value: value, label: this.props.languages[value]};
-        }.bind(this));
+    }
+    render () {
+        const languageOptions = Object.keys(this.props.languages).map(value => ({
+            value: value,
+            label: this.props.languages[value]
+        }));
         return (
-            <Form className={classes}>
-                <Select name="language"
-                        options={languageOptions}
-                        value={this.props.locale}
-                        onChange={this.onSetLanguage}
-                        required />
+            <Form className={classNames('language-chooser', this.props.className)}>
+                <Select
+                    required
+                    name="language"
+                    options={languageOptions}
+                    value={this.props.locale}
+                    onChange={this.handleSetLanguage}
+                />
             </Form>
         );
     }
-});
+}
+
+LanguageChooser.propTypes = {
+    className: PropTypes.string,
+    languages: PropTypes.object, // eslint-disable-line react/forbid-prop-types
+    locale: PropTypes.string
+};
+
+LanguageChooser.defaultProps = {
+    languages: languages,
+    locale: 'en'
+};
 
 module.exports = LanguageChooser;
diff --git a/src/components/login/login.jsx b/src/components/login/login.jsx
index c7243d7a6..58586e5e5 100644
--- a/src/components/login/login.jsx
+++ b/src/components/login/login.jsx
@@ -1,66 +1,108 @@
-var React = require('react');
-var FormattedMessage = require('react-intl').FormattedMessage;
+const bindAll = require('lodash.bindall');
+const FormattedMessage = require('react-intl').FormattedMessage;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var log = require('../../lib/log.js');
+const log = require('../../lib/log.js');
 
-var Form = require('../forms/form.jsx');
-var Input = require('../forms/input.jsx');
-var Button = require('../forms/button.jsx');
-var Spinner = require('../spinner/spinner.jsx');
+const Form = require('../forms/form.jsx');
+const Input = require('../forms/input.jsx');
+const Button = require('../forms/button.jsx');
+const Spinner = require('../spinner/spinner.jsx');
 
 require('./login.scss');
 
-var Login = React.createClass({
-    type: 'Login',
-    propTypes: {
-        onLogIn: React.PropTypes.func,
-        error: React.PropTypes.string
-    },
-    getInitialState: function () {
-        return {
+class Login extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleSubmit'
+        ]);
+        this.state = {
             waiting: false
         };
-    },
-    handleSubmit: function (formData) {
+    }
+    handleSubmit (formData) {
         this.setState({waiting: true});
-        this.props.onLogIn(formData, function (err) {
+        this.props.onLogIn(formData, err => {
             if (err) log.error(err);
             this.setState({waiting: false});
-        }.bind(this));
-    },
-    render: function () {
-        var error;
+        });
+    }
+    render () {
+        let error;
         if (this.props.error) {
             error = <div className="error">{this.props.error}</div>;
         }
         return (
             <div className="login">
                 <Form onSubmit={this.handleSubmit}>
-                    <label htmlFor="username" key="usernameLabel">
-                        <FormattedMessage id='general.username' />
+                    <label
+                        htmlFor="username"
+                        key="usernameLabel"
+                    >
+                        <FormattedMessage id="general.username" />
                     </label>
-                    <Input type="text" ref="username" name="username" maxLength="30" key="usernameInput" required />
-                    <label htmlFor="password" key="passwordLabel">
-                        <FormattedMessage id='general.password' />
+                    <Input
+                        required
+                        key="usernameInput"
+                        maxLength="30"
+                        name="username"
+                        ref={input => {
+                            this.username = input;
+                        }}
+                        type="text"
+                    />
+                    <label
+                        htmlFor="password"
+                        key="passwordLabel"
+                    >
+                        <FormattedMessage id="general.password" />
                     </label>
-                    <Input type="password" ref="password" name="password" key="passwordInput" required />
+                    <Input
+                        required
+                        key="passwordInput"
+                        name="password"
+                        ref={input => {
+                            this.password = input;
+                        }}
+                        type="password"
+                    />
                     {this.state.waiting ? [
-                        <Button className="submit-button white" type="submit" disabled="disabled" key="submitButton">
+                        <Button
+                            className="submit-button white"
+                            disabled="disabled"
+                            key="submitButton"
+                            type="submit"
+                        >
                             <Spinner />
                         </Button>
                     ] : [
-                        <Button className="submit-button white" type="submit" key="submitButton">
-                            <FormattedMessage id='general.signIn' />
+                        <Button
+                            className="submit-button white"
+                            key="submitButton"
+                            type="submit"
+                        >
+                            <FormattedMessage id="general.signIn" />
                         </Button>
                     ]}
-                    <a className="right" href="/accounts/password_reset/" key="passwordResetLink">
-                        <FormattedMessage id='login.needHelp' />
+                    <a
+                        className="right"
+                        href="/accounts/password_reset/"
+                        key="passwordResetLink"
+                    >
+                        <FormattedMessage id="login.needHelp" />
                     </a>
                     {error}
                 </Form>
             </div>
         );
     }
-});
+}
+
+Login.propTypes = {
+    error: PropTypes.string,
+    onLogIn: PropTypes.func
+};
 
 module.exports = Login;
diff --git a/src/components/masonrygrid/masonrygrid.jsx b/src/components/masonrygrid/masonrygrid.jsx
index ef32172e5..5f654488d 100644
--- a/src/components/masonrygrid/masonrygrid.jsx
+++ b/src/components/masonrygrid/masonrygrid.jsx
@@ -1,60 +1,68 @@
-var classNames = require('classnames');
-var React = require('react');
-var MediaQuery = require('react-responsive');
-var frameless = require('../../lib/frameless');
+const bindAll = require('lodash.bindall');
+const classNames = require('classnames');
+const MediaQuery = require('react-responsive').default;
+const PropTypes = require('prop-types');
+const React = require('react');
+
+const frameless = require('../../lib/frameless');
 
 require('./masonrygrid.scss');
 
-var MasonryGrid = React.createClass({
-    type: 'MasonryGrid',
-    getDefaultProps: function () {
-        return {
-            as: 'div'
-        };
-    },
-    reorderColumns: function (items, cols) {
-        var a1 = [];
-        var a2 = [];
-        var a3 = [];
-        var i = 0;
-        //only implemented for 2 and 3 columns so far - easy to extend if needed
+class MasonryGrid extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'reorderColumns'
+        ]);
+    }
+    reorderColumns (items, cols) {
+        const a1 = [];
+        const a2 = [];
+        const a3 = [];
+        let i = 0;
+        // only implemented for 2 and 3 columns so far - easy to extend if needed
         if (cols > 1 && cols < 4) {
-            for (i=0;i<items.length;i++){
-                var col = (i+cols)%cols;
+            for (i = 0; i < items.length; i++){
+                const col = (i + cols) % cols;
                 if (col === 0) {
                     a1.push(items[i]);
-                }
-                else if (col === 1) {
+                } else if (col === 1) {
                     a2.push(items[i]);
-                }
-                else if (col === 2) {
+                } else if (col === 2) {
                     a3.push(items[i]);
                 }
             }
-            return a1.concat(a2,a3);
-        } else {
-            return items;
+            return a1.concat(a2, a3);
         }
-    },
-    render: function () {
-        var classes = classNames(
-            'masonry',
-            this.props.className
-        );
+        return items;
+    }
+    render () {
         return (
-            <this.props.as className={classes}>
-                <MediaQuery maxWidth={frameless.tablet - 1} >
+            <this.props.as className={classNames('masonry', this.props.className)}>
+                <MediaQuery maxWidth={frameless.tablet - 1}>
                     {this.props.children}
                 </MediaQuery>
-                <MediaQuery minWidth={frameless.tablet} maxWidth={frameless.desktop - 1} >
+                <MediaQuery
+                    maxWidth={frameless.desktop - 1}
+                    minWidth={frameless.tablet}
+                >
                     {this.reorderColumns(this.props.children, 2)}
                 </MediaQuery>
-                <MediaQuery minWidth={frameless.desktop} >
+                <MediaQuery minWidth={frameless.desktop}>
                     {this.reorderColumns(this.props.children, 3)}
                 </MediaQuery>
             </this.props.as>
         );
     }
-});
+}
+
+MasonryGrid.propTypes = {
+    children: PropTypes.node,
+    className: PropTypes.string
+};
+
+MasonryGrid.defaultProps = {
+    as: 'div'
+};
 
 module.exports = MasonryGrid;
diff --git a/src/components/microworld/microworld.jsx b/src/components/microworld/microworld.jsx
index ec21d5a87..5923f7ac4 100644
--- a/src/components/microworld/microworld.jsx
+++ b/src/components/microworld/microworld.jsx
@@ -1,39 +1,53 @@
-var React = require('react');
+const bindAll = require('lodash.bindall');
+const PropTypes = require('prop-types');
+const React = require('react');
+
+const Box = require('../box/box.jsx');
+const LegacyCarousel = require('../carousel/legacy-carousel.jsx');
+const IframeModal = require('../modal/iframe/modal.jsx');
+const NestedCarousel = require('../nestedcarousel/nestedcarousel.jsx');
 
 require('./microworld.scss');
 
-var Box = require('../box/box.jsx');
-var LegacyCarousel = require('../carousel/legacy-carousel.jsx');
-var IframeModal = require('../modal/iframe/modal.jsx');
-var NestedCarousel = require('../nestedcarousel/nestedcarousel.jsx');
-
-var Microworld = React.createClass({
-    type: 'Microworld',
-    propTypes: {
-        microworldData: React.PropTypes.node.isRequired
-    },
-    markVideoOpen: function (key) {
-        {/* When a video is clicked, mark it as an open video, so the video Modal will open.
-        Key is the number of the video, so distinguish between different videos on the page */}
-
-        var videoOpenArr = this.state.videoOpen;
-        videoOpenArr[key] = true;
-        this.setState({videoOpen: videoOpenArr});
-    },
-    markVideoClosed: function (key) {
-        {/* When a video's x is clicked, mark it as closed, so the video Modal will disappear.
-        Key is the number of the video, so distinguish between different videos on the page */}
-        var videoOpenArr = this.state.videoOpen;
-        videoOpenArr[key] = false;
-        this.setState({videoOpen: videoOpenArr});
-    },
-    getInitialState: function () {
-        return {
+class Microworld extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'markVideoOpen',
+            'markVideoClosed',
+            'renderVideos',
+            'renderVideo',
+            'renderEditorWindow',
+            'renderTips',
+            'renderStarterProject',
+            'renderProjectIdeasBox',
+            'renderForum',
+            'renderDesignStudio'
+        ]);
+        this.state = {
             videoOpen: {}
         };
-    },
-    renderVideos: function () {
-        var videos = this.props.microworldData.videos;
+    }
+    markVideoOpen (key) {
+        /* 
+            When a video is clicked, mark it as an open video, so the video Modal will open.
+            Key is the number of the video, so distinguish between different videos on the page
+        */
+        const videoOpenArr = this.state.videoOpen;
+        videoOpenArr[key] = true;
+        this.setState({videoOpen: videoOpenArr});
+    }
+    markVideoClosed (key) {
+        /*
+            When a video's x is clicked, mark it as closed, so the video Modal will disappear.
+            Key is the number of the video, so distinguish between different videos on the page
+        */
+        const videoOpenArr = this.state.videoOpen;
+        videoOpenArr[key] = false;
+        this.setState({videoOpen: videoOpenArr});
+    }
+    renderVideos () {
+        const videos = this.props.microworldData.videos;
         if (!videos || videos.length <= 0) {
             return null;
         }
@@ -48,26 +62,32 @@ var Microworld = React.createClass({
                 </div>
             </div>
         );
-    },
-    renderVideo: function (video, key) {
+    }
+    renderVideo (video, key) {
         return (
             <div>
                 <div className="video">
-                    <div className="play-button" onClick={this.markVideoOpen.bind(this, key)}>
-                    </div>
+                    <div
+                        className="play-button"
+                        onClick={() => { // eslint-disable-line react/jsx-no-bind
+                            this.markVideoOpen(key);
+                        }}
+                    />
                     <img src={video.image} />
                 </div>
                 <IframeModal
                     className="mod-microworld-video"
                     isOpen={this.state.videoOpen[key]}
-                    onRequestClose={this.markVideoClosed.bind(this, key)}
                     src={video.link}
+                    onRequestClose={() => { // eslint-disable-line react/jsx-no-bind
+                        this.markVideoClosed(key);
+                    }}
                 />
             </div>
         );
-    },
-    renderEditorWindow: function () {
-        var projectId = this.props.microworldData.microworld_project_id;
+    }
+    renderEditorWindow () {
+        const projectId = this.props.microworldData.microworld_project_id;
         
         if (!projectId) {
             return null;
@@ -75,30 +95,37 @@ var Microworld = React.createClass({
         return (
             <div className="editor section">
                 <h1 className="sectionheader">Start Creating!</h1>
-                <iframe src={'//scratch.mit.edu/projects/embed-editor/' + projectId + '/?isMicroworld=true'}
-                        frameBorder="0"> </iframe>
+                <iframe
+                    frameBorder="0"
+                    src={`//scratch.mit.edu/projects/embed-editor/${projectId}/?isMicroworld=true`}
+                />
                 {this.renderTips()}
             </div>
         );
-    },
-    renderTips: function () {
-        var tips =  this.props.microworldData.tips;
+    }
+    renderTips () {
+        const tips = this.props.microworldData.tips;
         if (!tips || tips.length <= 0) {
             return null;
         }
 
         return (
             <div className="box nestedcarousel">
-                <div className="box-header">
-                </div>
+                <div className="box-header" />
                 <div className="box-content">
-                    <NestedCarousel items={tips} settings={{slidesToShow:1,slidesToScroll:1}}/>
+                    <NestedCarousel
+                        items={tips}
+                        settings={{
+                            slidesToShow: 1,
+                            slidesToScroll: 1
+                        }}
+                    />
                 </div>
             </div>
-            );
-    },
-    renderStarterProject: function () {
-        var starterProjects = this.props.microworldData.starter_projects;
+        );
+    }
+    renderStarterProject () {
+        const starterProjects = this.props.microworldData.starter_projects;
         if (!starterProjects || starterProjects.length <= 0){
             return null;
         }
@@ -107,39 +134,42 @@ var Microworld = React.createClass({
             <div className="project-ideas">
                 <h1 className="sectionheader">Check out ideas for more projects</h1>
                 <Box
+                    key="starter_projects"
                     title="More Starter Projects"
-                    key="starter_projects">
+                >
                     <LegacyCarousel items={starterProjects} />
                 </Box>
             </div>
         );
-    },
-    renderProjectIdeasBox: function () {
-        var communityProjects = this.props.microworldData.community_projects;
+    }
+    renderProjectIdeasBox () {
+        const communityProjects = this.props.microworldData.community_projects;
         if (!communityProjects || communityProjects.size <= 0) {
             return null;
         }
 
-        var featured = communityProjects.featured_projects;
-        var all = communityProjects.newest_projects;
+        const featured = communityProjects.featured_projects;
+        const all = communityProjects.newest_projects;
 
-        var rows = [];
+        const rows = [];
         if (featured && featured.length > 0){
             rows.push(
                 <Box
+                    key="community_featured_projects"
                     title="Featured Community Projects"
-                    key="community_featured_projects">
+                >
                     <LegacyCarousel items={featured} />
-               </Box>
+                </Box>
             );
         }
         if (all && all.length > 0) {
             rows.push(
                 <Box
-                     title="All Community Projects"
-                     key="community_all_projects">
-                     <LegacyCarousel items={all} />
-               </Box>
+                    key="community_all_projects"
+                    title="All Community Projects"
+                >
+                    <LegacyCarousel items={all} />
+                </Box>
             );
         }
         if (rows.length <= 0) {
@@ -151,67 +181,88 @@ var Microworld = React.createClass({
                 {rows}
             </div>
         );
-    },
-    renderForum: function () {
+    }
+    renderForum () {
         if (!this.props.microworldData.show_forum) {
             return null;
         }
 
         return (
-        <div className="forum">
-            <h1 className="sectionheader">Chat with others!</h1>
-            <img src="/images/forum-image.png"/>
-        </div>
+            <div className="forum">
+                <h1 className="sectionheader">Chat with others!</h1>
+                <img src="/images/forum-image.png" />
+            </div>
         );
-    },
-    renderDesignStudio: function () {
-        var designChallenge = this.props.microworldData.design_challenge;
+    }
+    renderDesignStudio () {
+        const designChallenge = this.props.microworldData.design_challenge;
         if (!designChallenge) {
             return null;
         }
+
+        let studioHref = '';
         if (designChallenge.studio_id) {
-            var studioHref = 'https://scratch.mit.edu//studios/' + designChallenge.studio_id + '/';
+            studioHref = `https://scratch.mit.edu//studios/${designChallenge.studio_id}/`;
         }
         if (designChallenge.project_id) {
             return (
                 <div className="side-by-side section">
                     <h1 className="sectionheader">Join our Design Challenge!</h1>
                     <div className="design-studio">
-                        <iframe src={'https://scratch.mit.edu/projects/' + designChallenge.project_id +
-                                     '/#fullscreen'} frameBorder="0"> </iframe>
+                        <iframe
+                            frameBorder="0"
+                            src={`https://scratch.mit.edu/projects/${designChallenge.project_id}/#fullscreen`}
+                        />
                     </div>
                     <div className="design-studio-projects">
-                        <Box title="Examples"
-                             key="scratch_design_studio"
-                             moreTitle={studioHref ? 'Visit the studio' : null}
-                             moreHref={studioHref ? studioHref : null}>
+                        <Box
+                            key="scratch_design_studio"
+                            moreHref={studioHref ? studioHref : null}
+                            moreTitle={studioHref ? 'Visit the studio' : null}
+                            title="Examples"
+                        >
                             {/* The two carousels are used to show two rows of projects, one above the
                                 other. This should be probably be changed, to allow better scrolling. */}
-                            <LegacyCarousel settings={{slidesToShow:2,slidesToScroll:2}}
-                                      items={this.props.microworldData.design_challenge.studio1} />
-                            <LegacyCarousel settings={{slidesToShow:2,slidesToScroll:2}}
-                                      items={this.props.microworldData.design_challenge.studio2} />
+                            <LegacyCarousel
+                                items={this.props.microworldData.design_challenge.studio1}
+                                settings={{
+                                    slidesToShow: 2,
+                                    slidesToScroll: 2
+                                }}
+                            />
+                            <LegacyCarousel
+                                items={this.props.microworldData.design_challenge.studio2}
+                                settings={{
+                                    slidesToShow: 2,
+                                    slidesToScroll: 2
+                                }}
+                            />
                         </Box>
                     </div>
                 </div>
             );
-        } else {
-            return (
-                <div className="section">
-                    <h1 className="sectionheader">Join our Design Challenge!</h1>
-                    <Box
-                        title="design Challenge Projects"
-                        key="scratch_design_studio"
-                        moreTitle={studioHref ? 'Visit the studio' : null}
-                        moreHref={studioHref ? studioHref : null}>
-                        <LegacyCarousel items={this.props.microworldData.design_challenge.studio1.concat(
-                            this.props.microworldData.design_challenge.studio2)} />
-                   </Box>
-                </div>
-            );
         }
-    },
-    render: function () {
+        return (
+            <div className="section">
+                <h1 className="sectionheader">Join our Design Challenge!</h1>
+                <Box
+                    key="scratch_design_studio"
+                    moreHref={studioHref ? studioHref : null}
+                    moreTitle={studioHref ? 'Visit the studio' : null}
+                    title="design Challenge Projects"
+                >
+                    <LegacyCarousel
+                        items={
+                            this.props.microworldData.design_challenge.studio1.concat(
+                                this.props.microworldData.design_challenge.studio2
+                            )
+                        }
+                    />
+               `</Box>
+            </div>
+        );
+    }
+    render () {
         return (
             <div className="inner microworld">
                 <div className="top-banner section">
@@ -231,6 +282,10 @@ var Microworld = React.createClass({
 
         );
     }
-});
+}
+
+Microworld.propTypes = {
+    microworldData: PropTypes.node.isRequired
+};
 
 module.exports = Microworld;
diff --git a/src/components/modal/base/modal.jsx b/src/components/modal/base/modal.jsx
index 0c0647144..2d9fdc05e 100644
--- a/src/components/modal/base/modal.jsx
+++ b/src/components/modal/base/modal.jsx
@@ -1,56 +1,65 @@
-var classNames = require('classnames');
-var omit = require('lodash.omit');
-var React = require('react');
-var ReactModal = require('react-modal');
+const bindAll = require('lodash.bindall');
+const classNames = require('classnames');
+const omit = require('lodash.omit');
+const PropTypes = require('prop-types');
+const React = require('react');
+const ReactModal = require('react-modal');
 
 require('./modal.scss');
 
-ReactModal.setAppElement(document.getElementById('view'));
-
 /**
  * Container for pop up windows (See: registration window)
  */
-var Modal = React.createClass({
-    type: 'Modal',
-    propTypes: {
-        className: React.PropTypes.string,
-        overlayClassName: React.PropTypes.string
-    },
-    requestClose: function () {
+class Modal extends React.Component {
+    constructor (props) {
+        super(props);
+        // ReactModal.setAppElement(document.getElementById('view'));
+        bindAll(this, [
+            'handleRequestClose'
+        ]);
+    }
+    handleRequestClose () {
         return this.modal.portal.requestClose();
-    },
-    render: function () {
-        var modalClasses = classNames(
-            'modal-content',
-            this.props.className
-        );
-        var overlayClasses = classNames(
-            'modal-overlay',
-            this.props.overlayClassName
-        );
-        
+    }
+    render () {
         return (
             <ReactModal
-                ref={
-                    function (component) {
-                        this.modal = component;
-                    }.bind(this)
-                }
-                className={modalClasses}
-                overlayClassName={overlayClasses}
+                appElement={document.getElementById('view')}
+                className={{
+                    base: classNames('modal-content', this.props.className),
+                    afterOpen: classNames('modal-content', this.props.className),
+                    beforeClose: classNames('modal-content', this.props.className)
+                }}
+                overlayClassName={{
+                    base: classNames('modal-overlay', this.props.overlayClassName),
+                    afterOpen: classNames('modal-overlay', this.props.overlayClassName),
+                    beforeClose: classNames('modal-overlay', this.props.overlayClassName)
+                }}
+                ref={component => {
+                    this.modal = component;
+                }}
                 {...omit(this.props, ['className', 'overlayClassName'])}
             >
-                <div className="modal-content-close" onClick={this.requestClose}>
+                <div
+                    className="modal-content-close"
+                    onClick={this.handleRequestClose}
+                >
                     <img
+                        alt="close-icon"
                         className="modal-content-close-img"
                         src="/svgs/modal/close-x.svg"
-                        alt="close-icon"
                     />
                 </div>
                 {this.props.children}
             </ReactModal>
         );
     }
-});
+}
+
+Modal.propTypes = {
+    children: PropTypes.node,
+    className: PropTypes.string,
+    overlayClassName: PropTypes.string
+};
 
 module.exports = Modal;
diff --git a/src/components/modal/iframe/modal.jsx b/src/components/modal/iframe/modal.jsx
index 6bff74ee5..620e761f8 100644
--- a/src/components/modal/iframe/modal.jsx
+++ b/src/components/modal/iframe/modal.jsx
@@ -1,34 +1,28 @@
-var classNames = require('classnames');
-var omit = require('lodash.omit');
-var React = require('react');
+const classNames = require('classnames');
+const omit = require('lodash.omit');
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var Modal = require('../base/modal.jsx');
+const Modal = require('../base/modal.jsx');
 
 require('./modal.scss');
 
-var IframeModal = React.createClass({
-    propTypes: {
-        isOpen: React.PropTypes.bool,
-        onRequestClose: React.PropTypes.func,
-        className: React.PropTypes.string,
-        componentRef: React.PropTypes.func,
-        src: React.PropTypes.string
-    },
-    render: function () {
-        var iframeClasses = classNames(
-            'modal-content-iframe',
-            this.props.className
-        );
-        return (
-            <Modal {...omit(this.props, ['src'])}>
-                <iframe
-                    ref={this.props.componentRef}
-                    src={this.props.src}
-                    className={iframeClasses}
-                />
-            </Modal>
-        );
-    }
-});
+const IframeModal = props => (
+    <Modal {...omit(props, ['src'])}>
+        <iframe
+            className={classNames('modal-content-iframe', props.className)}
+            ref={props.componentRef}
+            src={props.src}
+        />
+    </Modal>
+);
+
+IframeModal.propTypes = {
+    className: PropTypes.string,
+    componentRef: PropTypes.func,
+    isOpen: PropTypes.bool,
+    onRequestClose: PropTypes.func,
+    src: PropTypes.string
+};
 
 module.exports = IframeModal;
diff --git a/src/components/modal/ttt/modal.jsx b/src/components/modal/ttt/modal.jsx
index f70dcd173..698d15d2b 100644
--- a/src/components/modal/ttt/modal.jsx
+++ b/src/components/modal/ttt/modal.jsx
@@ -1,121 +1,138 @@
-var FormattedMessage = require('react-intl').FormattedMessage;
-var MediaQuery = require('react-responsive');
-var omit = require('lodash.omit');
-var React = require('react');
+const FormattedMessage = require('react-intl').FormattedMessage;
+const MediaQuery = require('react-responsive').default;
+const omit = require('lodash.omit');
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var FlexRow = require('../../flex-row/flex-row.jsx');
-var frameless = require('../../../lib/frameless');
-var Modal = require('../base/modal.jsx');
-var TitleBanner = require('../../title-banner/title-banner.jsx');
+const FlexRow = require('../../flex-row/flex-row.jsx');
+const frameless = require('../../../lib/frameless');
+const Modal = require('../base/modal.jsx');
+const TitleBanner = require('../../title-banner/title-banner.jsx');
 
 require('../../forms/button.scss');
 require('./modal.scss');
 
-var TTTModal = React.createClass({
-    propTypes: {
-        title: React.PropTypes.string.isRequired,
-        description: React.PropTypes.string.isRequired,
-        tutorialLoc: React.PropTypes.string.isRequired,
-        activityLoc: React.PropTypes.string.isRequired,
-        guideLoc: React.PropTypes.string.isRequired,
-        thumbUrl: React.PropTypes.string.isRequired,
-        bannerUrl: React.PropTypes.string.isRequired
-    },
-    render: function () {
-        var modalOmit = [
-            'title',
-            'description',
-            'tutorialLoc',
-            'activityLoc',
-            'guideLoc',
-            'thumbUrl',
-            'bannerUrl'
-        ];
-        return (
-            <Modal
-                className="mod-ttt"
-                {...omit(this.props, modalOmit)}
+const TTTModal = props => (
+    <Modal
+        className="mod-ttt"
+        {...omit(
+            props,
+            [
+                'title',
+                'description',
+                'tutorialLoc',
+                'activityLoc',
+                'guideLoc',
+                'thumbUrl',
+                'bannerUrl'
+            ]
+        )}
+    >
+        <TitleBanner className="mod-ttt">
+            <MediaQuery minWidth={frameless.mobile}>
+                <img
+                    alt=""
+                    className="mod-ttt-img"
+                    src={props.bannerUrl}
+                />
+            </MediaQuery>
+            <MediaQuery maxWidth={frameless.mobile - 1}>
+                <img
+                    alt=""
+                    className="mod-ttt-img"
+                    src={props.thumbUrl}
+                />
+            </MediaQuery>
+        </TitleBanner>
+        <div className="ttt-title">
+            <h2>{props.title}</h2>
+            <p className="ttt-description">{props.description}</p>
+        </div>
+        <ul className="modal-content-ttt">
+            <FlexRow
+                as="li"
+                className="mod-ttt-item"
             >
-                <TitleBanner className="mod-ttt">
-                    <MediaQuery minWidth={frameless.mobile}>
-                        <img className="mod-ttt-img" src={this.props.bannerUrl} alt="" />
-                    </MediaQuery>
-                    <MediaQuery maxWidth={frameless.mobile - 1}>
-                        <img className="mod-ttt-img" src={this.props.thumbUrl} alt="" />
-                    </MediaQuery>
-                </TitleBanner>
-                <div className="ttt-title">
-                    <h2>{this.props.title}</h2>
-                    <p className="ttt-description">{this.props.description}</p>
+                <div className="modal-content-ttt-text">
+                    <div className="modal-content-ttt-title">
+                        <img
+                            alt="tutorial-icon"
+                            className="modal-content-ttt-title-img"
+                            src="/svgs/ttt/tutorial.svg"
+                        />
+                        <FormattedMessage id="ttt.tutorial" />
+                    </div>
+                    <p className="modal-content-ttt-subtitle">
+                        <FormattedMessage id="ttt.tutorialSubtitle" />
+                    </p>
                 </div>
-                <ul className="modal-content-ttt">
-                    <FlexRow as="li" className="mod-ttt-item">
-                        <div className="modal-content-ttt-text">
-                            <div className="modal-content-ttt-title">
-                                <img
-                                    className="modal-content-ttt-title-img"
-                                    src="/svgs/ttt/tutorial.svg"
-                                    alt="tutorial-icon"
-                                />
-                                <FormattedMessage id="ttt.tutorial" />
-                            </div>
-                            <p className="modal-content-ttt-subtitle">
-                                <FormattedMessage id="ttt.tutorialSubtitle" />
-                            </p>
-                        </div>
-                        <a
-                            href={this.props.tutorialLoc}
-                            className="button white mod-ttt-item"
-                        >
-                            <FormattedMessage id="tile.tryIt" />
-                        </a>
-                    </FlexRow>
-                    <FlexRow as="li" className="mod-ttt-item">
-                        <div className="modal-content-ttt-text">
-                            <div className="modal-content-ttt-title">
-                                <img
-                                    className="modal-content-ttt-title-img"
-                                    src="/svgs/ttt/activity-cards.svg"
-                                    alt="activity-cards-icon"
-                                />
-                                <FormattedMessage id="ttt.activityTitle" />
-                            </div>
-                            <p className="modal-content-ttt-subtitle">
-                                <FormattedMessage id="ttt.activitySubtitle" />
-                            </p>
-                        </div>
-                        <a
-                            href={this.props.activityLoc}
-                            className="button white mod-ttt-item"
-                        >
-                            <FormattedMessage id="ttt.open" />
-                        </a>
-                    </FlexRow>
-                    <FlexRow as="li" className="mod-ttt-item">
-                        <div className="modal-content-ttt-text">
-                            <div className="modal-content-ttt-title">
-                                <img
-                                    className="modal-content-ttt-title-img"
-                                    src="/svgs/ttt/educator-guide.svg"
-                                    alt="educator-guide-icon"
-                                />
-                                <FormattedMessage id="ttt.educatorTitle" />
-                            </div>
-                            <p className="modal-content-ttt-subtitle">
-                                <FormattedMessage id="ttt.educatorSubtitle" />
-                            </p>
-                        </div>
-                        <a
-                            href={this.props.guideLoc}
-                            className="button white mod-ttt-item"
-                        >
-                            <FormattedMessage id="ttt.open" />
-                        </a>
-                    </FlexRow>
-                </ul>
-            </Modal>
-        );
-    }
-});
+                <a
+                    className="button white mod-ttt-item"
+                    href={props.tutorialLoc}
+                >
+                    <FormattedMessage id="tile.tryIt" />
+                </a>
+            </FlexRow>
+            <FlexRow
+                as="li"
+                className="mod-ttt-item"
+            >
+                <div className="modal-content-ttt-text">
+                    <div className="modal-content-ttt-title">
+                        <img
+                            alt="activity-cards-icon"
+                            className="modal-content-ttt-title-img"
+                            src="/svgs/ttt/activity-cards.svg"
+                        />
+                        <FormattedMessage id="ttt.activityTitle" />
+                    </div>
+                    <p className="modal-content-ttt-subtitle">
+                        <FormattedMessage id="ttt.activitySubtitle" />
+                    </p>
+                </div>
+                <a
+                    className="button white mod-ttt-item"
+                    href={props.activityLoc}
+                >
+                    <FormattedMessage id="ttt.open" />
+                </a>
+            </FlexRow>
+            <FlexRow
+                as="li"
+                className="mod-ttt-item"
+            >
+                <div className="modal-content-ttt-text">
+                    <div className="modal-content-ttt-title">
+                        <img
+                            alt="educator-guide-icon"
+                            className="modal-content-ttt-title-img"
+                            src="/svgs/ttt/educator-guide.svg"
+                        />
+                        <FormattedMessage id="ttt.educatorTitle" />
+                    </div>
+                    <p className="modal-content-ttt-subtitle">
+                        <FormattedMessage id="ttt.educatorSubtitle" />
+                    </p>
+                </div>
+                <a
+                    className="button white mod-ttt-item"
+                    href={props.guideLoc}
+                >
+                    <FormattedMessage id="ttt.open" />
+                </a>
+            </FlexRow>
+        </ul>
+    </Modal>
+);
+
+TTTModal.propTypes = {
+    activityLoc: PropTypes.string.isRequired,
+    bannerUrl: PropTypes.string.isRequired,
+    description: PropTypes.string.isRequired,
+    guideLoc: PropTypes.string.isRequired,
+    thumbUrl: PropTypes.string.isRequired,
+    title: PropTypes.string.isRequired,
+    tutorialLoc: PropTypes.string.isRequired
+};
 
 module.exports = TTTModal;
diff --git a/src/components/navigation/base/navigation.jsx b/src/components/navigation/base/navigation.jsx
index 60e6d18f2..2b58f36e2 100644
--- a/src/components/navigation/base/navigation.jsx
+++ b/src/components/navigation/base/navigation.jsx
@@ -1,21 +1,18 @@
-var classNames = require('classnames');
-var React = require('react');
+const classNames = require('classnames');
+const PropTypes = require('prop-types');
+const React = require('react');
 
 require('./navigation.scss');
 
-var NavigationBox = React.createClass({
-    type: 'NavigationBox',
-    render: function () {
-        var classes = classNames(
-            'inner',
-            this.props.className
-        );
-        return (
-            <div className={classes}>
-                {this.props.children}
-            </div>
-        );
-    }
-});
+const NavigationBox = props => (
+    <div className={classNames('inner', props.className)}>
+        {props.children}
+    </div>
+);
+
+NavigationBox.propTypes = {
+    children: PropTypes.node,
+    className: PropTypes.string
+};
 
 module.exports = NavigationBox;
diff --git a/src/components/navigation/conference/2016/navigation.jsx b/src/components/navigation/conference/2016/navigation.jsx
index b9158c5b7..627f32abf 100644
--- a/src/components/navigation/conference/2016/navigation.jsx
+++ b/src/components/navigation/conference/2016/navigation.jsx
@@ -1,42 +1,55 @@
-var React = require('react');
+const React = require('react');
 
-var NavigationBox = require('../../base/navigation.jsx');
+const NavigationBox = require('../../base/navigation.jsx');
 
 require('./navigation.scss');
 
-var Navigation = React.createClass({
-    type: 'Navigation',
-    render: function () {
-        return (
-            <NavigationBox>
-                <ul className="ul mod-2016">
-                    <li className="li-left mod-logo mod-2016">
-                        <a href="/conference/2016" className="logo-a">
-                            <img
-                                src="/images/logo_sm.png"
-                                alt="Scratch Logo"
-                                className="logo-a-image"
-                            />
-                            <p className="logo-a-title">Conference</p>
+const Navigation = () => (
+    <NavigationBox>
+        <ul className="ul mod-2016">
+            <li className="li-left mod-logo mod-2016">
+                <a
+                    className="logo-a"
+                    href="/conference/2016"
+                >
+                    <img
+                        alt="Scratch Logo"
+                        className="logo-a-image"
+                        src="/images/logo_sm.png"
+                    />
+                    <p className="logo-a-title">Conference</p>
+                </a>
+            </li>
+            <li className="li-right mod-2016">
+                <ul className="li-right-ul mod-2016">
+                    <li className="link expect">
+                        <a
+                            className="link-a"
+                            href="/conference/2016/expect"
+                        >
+                            What to Expect
                         </a>
                     </li>
-                    <li className="li-right mod-2016">
-                        <ul className="li-right-ul mod-2016">
-                            <li className="link expect">
-                                <a href="/conference/2016/expect" className="link-a">What to Expect</a>
-                            </li>
-                            <li className="link plan">
-                                <a href="/conference/2016/plan" className="link-a">Plan Your Visit</a>
-                            </li>
-                            <li className="link schedule">
-                                <a href="/conference/2016/schedule" className="link-a">Schedule</a>
-                            </li>
-                        </ul>
+                    <li className="link plan">
+                        <a
+                            className="link-a"
+                            href="/conference/2016/plan"
+                        >
+                            Plan Your Visit
+                        </a>
+                    </li>
+                    <li className="link schedule">
+                        <a
+                            className="link-a"
+                            href="/conference/2016/schedule"
+                        >
+                            Schedule
+                        </a>
                     </li>
                 </ul>
-            </NavigationBox>
-        );
-    }
-});
+            </li>
+        </ul>
+    </NavigationBox>
+);
 
 module.exports = Navigation;
diff --git a/src/components/navigation/conference/2017/navigation.jsx b/src/components/navigation/conference/2017/navigation.jsx
index d3d554980..aaac430eb 100644
--- a/src/components/navigation/conference/2017/navigation.jsx
+++ b/src/components/navigation/conference/2017/navigation.jsx
@@ -1,29 +1,27 @@
-var React = require('react');
+const React = require('react');
 
-var NavigationBox = require('../../base/navigation.jsx');
+const NavigationBox = require('../../base/navigation.jsx');
 
 require('./navigation.scss');
 
-var Navigation = React.createClass({
-    type: 'Navigation',
-    render: function () {
-        return (
-            <NavigationBox>
-                <ul className="ul mod-2017">
-                    <li className="li-left mod-logo mod-2017">
-                        <a href="/conference" className="logo-a">
-                            <img
-                                src="/images/logo_sm.png"
-                                alt="Scratch Logo"
-                                className="logo-a-image"
-                            />
-                            <p className="logo-a-title">Conferences</p>
-                        </a>
-                    </li>
-                </ul>
-            </NavigationBox>
-        );
-    }
-});
+const Navigation = () => (
+    <NavigationBox>
+        <ul className="ul mod-2017">
+            <li className="li-left mod-logo mod-2017">
+                <a
+                    className="logo-a"
+                    href="/conference"
+                >
+                    <img
+                        alt="Scratch Logo"
+                        className="logo-a-image"
+                        src="/images/logo_sm.png"
+                    />
+                    <p className="logo-a-title">Conferences</p>
+                </a>
+            </li>
+        </ul>
+    </NavigationBox>
+);
 
 module.exports = Navigation;
diff --git a/src/components/navigation/conference/2018/navigation.jsx b/src/components/navigation/conference/2018/navigation.jsx
index eb4b39de8..d10870107 100644
--- a/src/components/navigation/conference/2018/navigation.jsx
+++ b/src/components/navigation/conference/2018/navigation.jsx
@@ -1,29 +1,27 @@
-var React = require('react');
+const React = require('react');
 
-var NavigationBox = require('../../base/navigation.jsx');
+const NavigationBox = require('../../base/navigation.jsx');
 
 require('./navigation.scss');
 
-var Navigation = React.createClass({
-    type: 'Navigation',
-    render: function () {
-        return (
-            <NavigationBox>
-                <ul className="ul mod-2018">
-                    <li className="li-left mod-logo mod-2018">
-                        <a href="/" className="logo-a">
-                            <img
-                                src="/images/logo_sm.png"
-                                alt="Scratch Logo"
-                                className="logo-a-image"
-                            />
-                            <p className="logo-a-title">Conferences</p>
-                        </a>
-                    </li>
-                </ul>
-            </NavigationBox>
-        );
-    }
-});
+const Navigation = () => (
+    <NavigationBox>
+        <ul className="ul mod-2018">
+            <li className="li-left mod-logo mod-2018">
+                <a
+                    className="logo-a"
+                    href="/"
+                >
+                    <img
+                        alt="Scratch Logo"
+                        className="logo-a-image"
+                        src="/images/logo_sm.png"
+                    />
+                    <p className="logo-a-title">Conferences</p>
+                </a>
+            </li>
+        </ul>
+    </NavigationBox>
+);
 
 module.exports = Navigation;
diff --git a/src/components/navigation/www/navigation.jsx b/src/components/navigation/www/navigation.jsx
index 4438718c6..fddfbafaf 100644
--- a/src/components/navigation/www/navigation.jsx
+++ b/src/components/navigation/www/navigation.jsx
@@ -1,31 +1,48 @@
-var classNames = require('classnames');
-var connect = require('react-redux').connect;
-var React = require('react');
-var ReactIntl = require('react-intl');
-var FormattedMessage = ReactIntl.FormattedMessage;
-var injectIntl = ReactIntl.injectIntl;
+const bindAll = require('lodash.bindall');
+const classNames = require('classnames');
+const connect = require('react-redux').connect;
+const FormattedMessage = require('react-intl').FormattedMessage;
+const injectIntl = require('react-intl').injectIntl;
+const intlShape = require('react-intl').intlShape;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var messageCountActions = require('../../../redux/message-count.js');
-var sessionActions = require('../../../redux/session.js');
+const messageCountActions = require('../../../redux/message-count.js');
+const sessionActions = require('../../../redux/session.js');
 
-var api = require('../../../lib/api');
-var Avatar = require('../../avatar/avatar.jsx');
-var Button = require('../../forms/button.jsx');
-var Dropdown = require('../../dropdown/dropdown.jsx');
-var Form = require('../../forms/form.jsx');
-var Input = require('../../forms/input.jsx');
-var log = require('../../../lib/log.js');
-var Login = require('../../login/login.jsx');
-var Modal = require('../../modal/base/modal.jsx');
-var NavigationBox = require('../base/navigation.jsx');
-var Registration = require('../../registration/registration.jsx');
+const api = require('../../../lib/api');
+const Avatar = require('../../avatar/avatar.jsx');
+const Button = require('../../forms/button.jsx');
+const Dropdown = require('../../dropdown/dropdown.jsx');
+const Form = require('../../forms/form.jsx');
+const Input = require('../../forms/input.jsx');
+const log = require('../../../lib/log.js');
+const Login = require('../../login/login.jsx');
+const Modal = require('../../modal/base/modal.jsx');
+const NavigationBox = require('../base/navigation.jsx');
+const Registration = require('../../registration/registration.jsx');
 
 require('./navigation.scss');
 
-var Navigation = React.createClass({
-    type: 'Navigation',
-    getInitialState: function () {
-        return {
+class Navigation extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'getProfileUrl',
+            'handleJoinClick',
+            'handleLoginClick',
+            'handleCloseLogin',
+            'handleLogIn',
+            'handleLogOut',
+            'handleAccountNavClick',
+            'handleCloseAccountNav',
+            'showCanceledDeletion',
+            'handleCloseCanceledDeletion',
+            'handleCloseRegistration',
+            'handleCompleteRegistration',
+            'handleSearchSubmit'
+        ]);
+        this.state = {
             accountNavOpen: false,
             canceledDeletionOpen: false,
             loginOpen: false,
@@ -33,155 +50,153 @@ var Navigation = React.createClass({
             registrationOpen: false,
             messageCountIntervalId: -1 // javascript method interval id for getting messsage count.
         };
-    },
-    getDefaultProps: function () {
-        return {
-            session: {},
-            unreadMessageCount: 0, // bubble number to display how many notifications someone has.
-            searchTerm: ''
-        };
-    },
-    componentDidMount: function () {
+    }
+    componentDidMount () {
         if (this.props.session.session.user) {
-            var intervalId = setInterval(function () {
-                this.props.dispatch(messageCountActions.getCount(this.props.session.session.user.username));
-            }.bind(this), 120000); // check for new messages every 2 mins.
-            this.setState({'messageCountIntervalId': intervalId});
+            const intervalId = setInterval(() => {
+                this.props.dispatch(
+                    messageCountActions.getCount(this.props.session.session.user.username)
+                );
+            }, 120000); // check for new messages every 2 mins.
+            this.setState({ // eslint-disable-line react/no-did-mount-set-state
+                messageCountIntervalId: intervalId
+            });
         }
-    },
-    componentDidUpdate: function (prevProps) {
-        if (prevProps.session.session.user != this.props.session.session.user) {
-            this.setState({
-                'loginOpen': false,
-                'accountNavOpen': false
+    }
+    componentDidUpdate (prevProps) {
+        if (prevProps.session.session.user !== this.props.session.session.user) {
+            this.setState({ // eslint-disable-line react/no-did-update-set-state
+                loginOpen: false,
+                accountNavOpen: false
             });
             if (this.props.session.session.user) {
-                var intervalId = setInterval(function () {
-                    this.props.dispatch(messageCountActions.getCount(this.props.session.session.user.username));
-                }.bind(this), 120000); // check for new messages every 2 mins.
-                this.setState({'messageCountIntervalId': intervalId});
+                const intervalId = setInterval(() => {
+                    this.props.dispatch(
+                        messageCountActions.getCount(this.props.session.session.user.username)
+                    );
+                }, 120000); // check for new messages every 2 mins.
+                this.setState({ // eslint-disable-line react/no-did-update-set-state
+                    messageCountIntervalId: intervalId
+                });
             } else {
                 // clear message count check, and set to default id.
                 clearInterval(this.state.messageCountIntervalId);
                 this.props.dispatch(messageCountActions.setCount(0));
-                this.setState({
-                    'messageCountIntervalId': -1
+                this.setState({ // eslint-disable-line react/no-did-update-set-state
+                    messageCountIntervalId: -1
                 });
             }
         }
-    },
-    componentWillUnmount: function () {
+    }
+    componentWillUnmount () {
         // clear message interval if it exists
-        if (this.state.messageCountIntervalId != -1) {
+        if (this.state.messageCountIntervalId !== -1) {
             clearInterval(this.state.messageCountIntervalId);
             this.props.dispatch(messageCountActions.setCount(0));
             this.setState({
-                'messageCountIntervalId': -1
+                messageCountIntervalId: -1
             });
         }
-    },
-    getProfileUrl: function () {
+    }
+    getProfileUrl () {
         if (!this.props.session.session.user) return;
-        return '/users/' + this.props.session.session.user.username + '/';
-    },
-    handleJoinClick: function (e) {
+        return `/users/${this.props.session.session.user.username}/`;
+    }
+    handleJoinClick (e) {
         e.preventDefault();
-        this.setState({'registrationOpen': true});
-    },
-    handleLoginClick: function (e) {
+        this.setState({registrationOpen: true});
+    }
+    handleLoginClick (e) {
         e.preventDefault();
-        this.setState({'loginOpen': !this.state.loginOpen});
-    },
-    closeLogin: function () {
-        this.setState({'loginOpen': false});
-    },
-    handleLogIn: function (formData, callback) {
-        this.setState({'loginError': null});
-        formData['useMessages'] = true;
+        this.setState({loginOpen: !this.state.loginOpen});
+    }
+    handleCloseLogin () {
+        this.setState({loginOpen: false});
+    }
+    handleLogIn (formData, callback) {
+        this.setState({loginError: null});
+        formData.useMessages = true;
         api({
             method: 'post',
             host: '',
             uri: '/accounts/login/',
             json: formData,
             useCsrf: true
-        }, function (err, body) {
-            if (err) this.setState({'loginError': err.message});
+        }, (err, body) => {
+            if (err) this.setState({loginError: err.message});
             if (body) {
                 body = body[0];
-                if (!body.success) {
+                if (body.success) {
+                    this.handleCloseLogin();
+                    body.messages.map(message => { // eslint-disable-line array-callback-return
+                        if (message.message === 'canceled-deletion') {
+                            this.showCanceledDeletion();
+                        }
+                    });
+                    this.props.dispatch(sessionActions.refreshSession());
+                } else {
                     if (body.redirect) {
                         window.location = body.redirect;
                     }
                     // Update login error message to a friendlier one if it exists
-                    this.setState({'loginError': body.msg});
-                } else {
-                    this.closeLogin();
-                    body.messages.map(function (message) {
-                        if (message.message == 'canceled-deletion') {
-                            this.showCanceledDeletion();
-                        }
-                    }.bind(this));
-                    this.props.dispatch(sessionActions.refreshSession());
+                    this.setState({loginError: body.msg});
                 }
             }
             // JS error already logged by api mixin
             callback();
-        }.bind(this));
-    },
-    handleLogOut: function (e) {
+        });
+    }
+    handleLogOut (e) {
         e.preventDefault();
         api({
             host: '',
             method: 'post',
             uri: '/accounts/logout/',
             useCsrf: true
-        }, function (err) {
+        }, err => {
             if (err) log.error(err);
-            this.closeLogin();
+            this.handleCloseLogin();
             window.location = '/';
-        }.bind(this));
-    },
-    handleAccountNavClick: function (e) {
+        });
+    }
+    handleAccountNavClick (e) {
         e.preventDefault();
-        this.setState({'accountNavOpen': true});
-    },
-    closeAccountNav: function () {
-        this.setState({'accountNavOpen': false});
-    },
-    showCanceledDeletion: function () {
-        this.setState({'canceledDeletionOpen': true});
-    },
-    closeCanceledDeletion: function () {
-        this.setState({'canceledDeletionOpen': false});
-    },
-    closeRegistration: function () {
-        this.setState({'registrationOpen': false});
-    },
-    completeRegistration: function () {
+        this.setState({accountNavOpen: true});
+    }
+    handleCloseAccountNav () {
+        this.setState({accountNavOpen: false});
+    }
+    showCanceledDeletion () {
+        this.setState({canceledDeletionOpen: true});
+    }
+    handleCloseCanceledDeletion () {
+        this.setState({canceledDeletionOpen: false});
+    }
+    handleCloseRegistration () {
+        this.setState({registrationOpen: false});
+    }
+    handleCompleteRegistration () {
         this.props.dispatch(sessionActions.refreshSession());
-        this.closeRegistration();
-    },
-    onSearchSubmit: function (formData) {
-        window.location.href = '/search/projects?q=' + encodeURIComponent(formData.q);
-    },
-    render: function () {
-        var classes = classNames({
-            'logged-in': this.props.session.session.user
-        });
-        var messageClasses = classNames({
-            'message-count': true,
-            'show': this.props.unreadMessageCount > 0
-        });
-        var dropdownClasses = classNames({
-            'user-info': true,
-            'open': this.state.accountNavOpen
-        });
-        var formatMessage = this.props.intl.formatMessage;
-        var createLink = this.props.session.session.user ? '/projects/editor/' : '/projects/editor/?tip_bar=home';
+        this.handleCloseRegistration();
+    }
+    handleSearchSubmit (formData) {
+        window.location.href = `/search/projects?q=${encodeURIComponent(formData.q)}`;
+    }
+    render () {
+        const createLink = this.props.session.session.user ? '/projects/editor/' : '/projects/editor/?tip_bar=home';
         return (
-            <NavigationBox className={classes}>
+            <NavigationBox
+                className={classNames({
+                    'logged-in': this.props.session.session.user
+                })}
+            >
                 <ul>
-                    <li className="logo"><a href="/" aria-label="Scratch"></a></li>
+                    <li className="logo">
+                        <a
+                            aria-label="Scratch"
+                            href="/"
+                        />
+                    </li>
 
                     <li className="link create">
                         <a href={createLink}>
@@ -205,47 +220,76 @@ var Navigation = React.createClass({
                     </li>
 
                     <li className="search">
-                        <Form onSubmit={this.onSearchSubmit}>
-                            <Button type="submit" className="btn-search" />
-                            <Input type="text"
-                                   value={this.props.searchTerm}
-                                   aria-label={formatMessage({id: 'general.search'})}
-                                   placeholder={formatMessage({id: 'general.search'})}
-                                   name="q" />
+                        <Form onSubmit={this.handleSearchSubmit}>
+                            <Button
+                                className="btn-search"
+                                type="submit"
+                            />
+                            <Input
+                                aria-label={this.props.intl.formatMessage({id: 'general.search'})}
+                                name="q"
+                                placeholder={this.props.intl.formatMessage({id: 'general.search'})}
+                                type="text"
+                                value={this.props.searchTerm}
+                            />
                         </Form>
                     </li>
                     {this.props.session.status === sessionActions.Status.FETCHED ? (
                         this.props.session.session.user ? [
-                            <li className="link right messages" key="messages">
+                            <li
+                                className="link right messages"
+                                key="messages"
+                            >
                                 <a
                                     href="/messages/"
-                                    title={formatMessage({id: 'general.messages'})}>
-
-                                    <span className={messageClasses}>{this.props.unreadMessageCount}</span>
+                                    title={this.props.intl.formatMessage({id: 'general.messages'})}
+                                >
+                                    <span
+                                        className={classNames({
+                                            'message-count': true,
+                                            'show': this.props.unreadMessageCount > 0
+                                        })}
+                                    >{this.props.unreadMessageCount}</span>
                                     <FormattedMessage id="general.messages" />
                                 </a>
                             </li>,
-                            <li className="link right mystuff" key="mystuff">
+                            <li
+                                className="link right mystuff"
+                                key="mystuff"
+                            >
                                 <a
                                     href="/mystuff/"
-                                    title={formatMessage({id: 'general.myStuff'})}>
-
+                                    title={this.props.intl.formatMessage({id: 'general.myStuff'})}
+                                >
                                     <FormattedMessage id="general.myStuff" />
                                 </a>
                             </li>,
-                            <li className="link right account-nav" key="account-nav">
-                                <a className={dropdownClasses}
-                                    href="#" onClick={this.handleAccountNavClick}>
-                                    <Avatar src={this.props.session.session.user.thumbnailUrl} alt="" />
-                                    <span className='profile-name'>
+                            <li
+                                className="link right account-nav"
+                                key="account-nav"
+                            >
+                                <a
+                                    className={classNames({
+                                        'user-info': true,
+                                        'open': this.state.accountNavOpen
+                                    })}
+                                    href="#"
+                                    onClick={this.handleAccountNavClick}
+                                >
+                                    <Avatar
+                                        alt=""
+                                        src={this.props.session.session.user.thumbnailUrl}
+                                    />
+                                    <span className="profile-name">
                                         {this.props.session.session.user.username}
                                     </span>
                                 </a>
                                 <Dropdown
-                                        as="ul"
-                                        isOpen={this.state.accountNavOpen}
-                                        onRequestClose={this.closeAccountNav}
-                                        className={process.env.SCRATCH_ENV}>
+                                    as="ul"
+                                    className={process.env.SCRATCH_ENV}
+                                    isOpen={this.state.accountNavOpen}
+                                    onRequestClose={this.handleCloseAccountNav}
+                                >
                                     <li>
                                         <a href={this.getProfileUrl()}>
                                             <FormattedMessage id="general.profile" />
@@ -264,8 +308,8 @@ var Navigation = React.createClass({
                                         </li>
                                     ] : []}
                                     {this.props.permissions.student ? [
-                                        <li>
-                                            <a href={'/classes/' + this.props.session.session.user.classroomId + '/'}>
+                                        <li key="my-class-li">
+                                            <a href={`/classes/${this.props.session.session.user.classroomId}/`}>
                                                 <FormattedMessage id="general.myClass" />
                                             </a>
                                         </li>
@@ -276,69 +320,123 @@ var Navigation = React.createClass({
                                         </a>
                                     </li>
                                     <li className="divider">
-                                        <a href="#" onClick={this.handleLogOut}>
+                                        <a
+                                            href="#"
+                                            onClick={this.handleLogOut}
+                                        >
                                             <FormattedMessage id="navigation.signOut" />
                                         </a>
                                     </li>
                                 </Dropdown>
                             </li>
                         ] : [
-                            <li className="link right join" key="join">
-                                <a href="#" onClick={this.handleJoinClick}>
+                            <li
+                                className="link right join"
+                                key="join"
+                            >
+                                <a
+                                    href="#"
+                                    onClick={this.handleJoinClick}
+                                >
                                     <FormattedMessage id="general.joinScratch" />
                                 </a>
                             </li>,
                             <Registration
-                                    key="registration"
-                                    isOpen={this.state.registrationOpen}
-                                    onRequestClose={this.closeRegistration}
-                                    onRegistrationDone={this.completeRegistration} />,
-                            <li className="link right login-item" key="login">
+                                isOpen={this.state.registrationOpen}
+                                key="registration"
+                                onRegistrationDone={this.handleCompleteRegistration}
+                                onRequestClose={this.handleCloseRegistration}
+                            />,
+                            <li
+                                className="link right login-item"
+                                key="login"
+                            >
                                 <a
-                                    href="#"
-                                    onClick={this.handleLoginClick}
                                     className="ignore-react-onclickoutside"
-                                    key="login-link">
-                                        <FormattedMessage id="general.signIn" />
-                               </a>
+                                    href="#"
+                                    key="login-link"
+                                    onClick={this.handleLoginClick}
+                                >
+                                    <FormattedMessage id="general.signIn" />
+                                </a>
                                 <Dropdown
-                                        className="login-dropdown with-arrow"
-                                        isOpen={this.state.loginOpen}
-                                        onRequestClose={this.closeLogin}
-                                        key="login-dropdown">
+                                    className="login-dropdown with-arrow"
+                                    isOpen={this.state.loginOpen}
+                                    key="login-dropdown"
+                                    onRequestClose={this.handleCloseLogin}
+                                >
                                     <Login
+                                        error={this.state.loginError}
                                         onLogIn={this.handleLogIn}
-                                        error={this.state.loginError} />
+                                    />
                                 </Dropdown>
                             </li>
-                        ]) : [
-                        ]}
+                        ]) : []}
                 </ul>
-                <Modal isOpen={this.state.canceledDeletionOpen}
-                       onRequestClose={this.closeCanceledDeletion}
-                       style={{content:{padding: 15}}}>
+                <Modal
+                    isOpen={this.state.canceledDeletionOpen}
+                    style={{
+                        content: {
+                            padding: 15
+                        }
+                    }}
+                    onRequestClose={this.handleCloseCanceledDeletion}
+                >
                     <h4>Your Account Will Not Be Deleted</h4>
+                    <h4><FormattedMessage id="general.noDeletionTitle" /></h4>
                     <p>
-                        Your account was scheduled for deletion but you logged in. Your account has been reactivated.
-                        If you didn’t request for your account to be deleted, you should
-                        {' '}<a href="/accounts/password_reset/">change your password</a>{' '}
-                        to make sure your account is secure.
+                        <FormattedMessage
+                            id="general.noDeletionDescription"
+                            values={{
+                                resetLink: <a href="/accounts/password_reset/">
+                                    {this.props.intl.formatMessage({id: 'general.noDeletionLink'})}
+                                </a>
+                            }}
+                        />
                     </p>
                 </Modal>
             </NavigationBox>
         );
     }
-});
+}
 
-var mapStateToProps = function (state) {
-    return {
-        session: state.session,
-        permissions: state.permissions,
-        unreadMessageCount: state.messageCount.messageCount,
-        searchTerm: state.navigation
-    };
+Navigation.propTypes = {
+    dispatch: PropTypes.func,
+    intl: intlShape,
+    permissions: PropTypes.shape({
+        admin: PropTypes.bool,
+        social: PropTypes.bool,
+        educator: PropTypes.bool,
+        educator_invitee: PropTypes.bool,
+        student: PropTypes.bool
+    }),
+    searchTerm: PropTypes.string,
+    session: PropTypes.shape({
+        session: PropTypes.shape({
+            user: PropTypes.shape({
+                classroomId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
+                thumbnailUrl: PropTypes.string,
+                username: PropTypes.string
+            })
+        }),
+        status: PropTypes.string
+    }),
+    unreadMessageCount: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
 };
 
-var ConnectedNavigation = connect(mapStateToProps)(Navigation);
+Navigation.defaultProps = {
+    session: {},
+    unreadMessageCount: 0, // bubble number to display how many notifications someone has.
+    searchTerm: ''
+};
+
+const mapStateToProps = state => ({
+    session: state.session,
+    permissions: state.permissions,
+    unreadMessageCount: state.messageCount.messageCount,
+    searchTerm: state.navigation
+});
+
+const ConnectedNavigation = connect(mapStateToProps)(Navigation);
 
 module.exports = injectIntl(ConnectedNavigation);
diff --git a/src/components/nestedcarousel/nestedcarousel.jsx b/src/components/nestedcarousel/nestedcarousel.jsx
index 4600032e3..fb5b41b8c 100644
--- a/src/components/nestedcarousel/nestedcarousel.jsx
+++ b/src/components/nestedcarousel/nestedcarousel.jsx
@@ -1,75 +1,84 @@
-var classNames = require('classnames');
-var defaults = require('lodash.defaults');
-var React = require('react');
-var Slider = require('react-slick');
+const classNames = require('classnames');
+const defaults = require('lodash.defaults');
+const PropTypes = require('prop-types');
+const React = require('react');
+const Slider = require('react-slick');
 
-var Thumbnail = require('../thumbnail/thumbnail.jsx');
+const Thumbnail = require('../thumbnail/thumbnail.jsx');
 
 require('slick-carousel/slick/slick.scss');
 require('slick-carousel/slick/slick-theme.scss');
 require('./nestedcarousel.scss');
 
 
-{/*
+/*
     NestedCarousel is used to show a carousel, where each slide is composed of a few
     thumbnails (for example, to show step-by-syep tips, where each stage has a few steps).
     It creates the thumbnails without links.
 
     Each slide has a title, and then a list of thumbnails, that will be shown together.
-*/}
-var NestedCarousel = React.createClass({
-    type: 'NestedCarousel',
-    propTypes: {
-        items: React.PropTypes.array
-    },
-    getDefaultProps: function () {
-        return {
-            items: require('./nestedcarousel.json')
-        };
-    },
-    render: function () {
-        var settings = this.props.settings || {};
-        defaults(settings, {
-            dots: true,
-            infinite: false,
-            lazyLoad: true,
-            slidesToShow: 1,
-            slidesToScroll: 1,
-            variableWidth: false
-        });
+*/
+const NestedCarousel = props => {
+    defaults(props.settings, {
+        dots: true,
+        infinite: false,
+        lazyLoad: true,
+        slidesToShow: 1,
+        slidesToScroll: 1,
+        variableWidth: false
+    });
         
-        var arrows = this.props.items.length > settings.slidesToShow;
-        
-        var classes = classNames(
-            'nestedcarousel',
-            'carousel',
-            this.props.className
-        );
-        
-        var stages = [];
-        for (var i=0; i < this.props.items.length; i++) {
-            var items = this.props.items[i].thumbnails;
-            var thumbnails = [];
-            for (var j=0; j < items.length; j++) {
-                var item = items[j];
-                thumbnails.push(
-                    <Thumbnail key={'inner_' + i + '_' + j}
-                               title={item.title}
-                               src={item.thumbnailUrl}
-                               linkTitle = {false} />);
-            }
-            stages.push(
-            <div key={'outer_' + i}>
-                <h3>{this.props.items[i].title}</h3>
-                {thumbnails}
-            </div>);
+    const arrows = props.items.length > props.settings.slidesToShow;
+    const stages = [];
+
+    for (let i = 0; i < props.items.length; i++) {
+        const items = props.items[i].thumbnails;
+        const thumbnails = [];
+        for (let j = 0; j < items.length; j++) {
+            const item = items[j];
+            thumbnails.push(
+                <Thumbnail
+                    key={`inner_${i}_${j}`}
+                    linkTitle={false}
+                    src={item.thumbnailUrl}
+                    title={item.title}
+                />
+            );
         }
-        return (
-            <Slider className={classes} arrows={arrows} {... settings}>
-                {stages}
-            </Slider>
+        stages.push(
+            <div key={`outer_${i}`}>
+                <h3>{props.items[i].title}</h3>
+                {thumbnails}
+            </div>
         );
     }
-});
+    return (
+        <Slider
+            arrows={arrows}
+            className={classNames('nestedcarousel', 'carousel', props.className)}
+            {...props.settings}
+        >
+            {stages}
+        </Slider>
+    );
+};
+
+NestedCarousel.propTypes = {
+    className: PropTypes.string,
+    items: PropTypes.arrayOf(PropTypes.object),
+    settings: PropTypes.shape({
+        dots: PropTypes.bool,
+        infinite: PropTypes.bool,
+        lazyLoad: PropTypes.bool,
+        slidesToShow: PropTypes.number,
+        slidesToScroll: PropTypes.number,
+        variableWidth: PropTypes.bool
+    })
+};
+
+NestedCarousel.defaultProps = {
+    settings: {},
+    items: require('./nestedcarousel.json')
+};
 
 module.exports = NestedCarousel;
diff --git a/src/components/news/news.jsx b/src/components/news/news.jsx
index 227e2e1a8..b951f4e3b 100644
--- a/src/components/news/news.jsx
+++ b/src/components/news/news.jsx
@@ -1,54 +1,53 @@
-var React = require('react');
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var Box = require('../box/box.jsx');
+const Box = require('../box/box.jsx');
 
 require('./news.scss');
 
-var News = React.createClass({
-    type: 'News',
-    propTypes: {
-        items: React.PropTypes.array
-    },
-    getDefaultProps: function () {
-        return {
-            items: require('./news.json'),
-            messages: {
-                'general.viewAll': 'View All',
-                'news.scratchNews': 'Scratch News'
-            }
-        };
-    },
-    render: function () {
-        return (
-            <Box
-                className="news"
-                title={this.props.messages['news.scratchNews']}
-                moreTitle={this.props.messages['general.viewAll']}
-                moreHref="/discuss/5/">
+const News = props => (
+    <Box
+        className="news"
+        moreHref="/discuss/5/"
+        moreTitle={props.messages['general.viewAll']}
+        title={props.messages['news.scratchNews']}
+    >
+        <ul>
+            {props.items.map(item => (
+                <li key={item.id}>
+                    <a href={item.url}>
+                        <img
+                            alt=""
+                            className="news-image"
+                            height="53"
+                            src={item.image}
+                            width="53"
+                        />
+                        <div className="news-description">
+                            <h4>{item.headline}</h4>
+                            <p>{item.copy}</p>
+                        </div>
+                    </a>
+                </li>
+            ))}
+        </ul>
+    </Box>
+);
 
-                <ul>
-                    {this.props.items.map(function (item) {
-                        return (
-                            <li key={item.id}>
-                                <a href={item.url}>
-                                    <img src={item.image}
-                                         className="news-image"
-                                         width="53"
-                                         height="53"
-                                         alt=""
-                                    />
-                                    <div className="news-description">
-                                        <h4>{item.headline}</h4>
-                                        <p>{item.copy}</p>
-                                    </div>
-                                </a>
-                            </li>
-                        );
-                    })}
-                </ul>
-            </Box>
-        );
+News.propTypes = {
+    items: PropTypes.arrayOf(PropTypes.object),
+    messages: PropTypes.shape({
+        'general.viewAll': PropTypes.string,
+        'news.scratchNews': PropTypes.string
+    })
+};
+
+News.defaultProps = {
+    items: require('./news.json'),
+    messages: {
+        'general.viewAll': 'View All',
+        'news.scratchNews': 'Scratch News'
     }
-});
+};
 
 module.exports = News;
diff --git a/src/components/page/conference/2016/page.jsx b/src/components/page/conference/2016/page.jsx
index 32b1e359e..fa8ff3a01 100644
--- a/src/components/page/conference/2016/page.jsx
+++ b/src/components/page/conference/2016/page.jsx
@@ -1,27 +1,27 @@
-var React = require('react');
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var Navigation = require('../../../navigation/conference/2016/navigation.jsx');
-var Footer = require('../../../footer/conference/2016/footer.jsx');
+const Navigation = require('../../../navigation/conference/2016/navigation.jsx');
+const Footer = require('../../../footer/conference/2016/footer.jsx');
 
 require('../page.scss');
 
-var Page = React.createClass({
-    type: 'Page',
-    render: function () {
-        return (
-            <div className="page mod-conference">
-                <div id="navigation">
-                    <Navigation />
-                </div>
-                <div id="view">
-                    {this.props.children}
-                </div>
-                <div id="footer">
-                    <Footer />
-                </div>
-            </div>
-        );
-    }
-});
+const Page = props => (
+    <div className="page mod-conference">
+        <div id="navigation">
+            <Navigation />
+        </div>
+        <div id="view">
+            {props.children}
+        </div>
+        <div id="footer">
+            <Footer />
+        </div>
+    </div>
+);
+
+Page.propTypes = {
+    children: PropTypes.node
+};
 
 module.exports = Page;
diff --git a/src/components/page/conference/2017/page.jsx b/src/components/page/conference/2017/page.jsx
index b0def2666..03e8757af 100644
--- a/src/components/page/conference/2017/page.jsx
+++ b/src/components/page/conference/2017/page.jsx
@@ -1,27 +1,27 @@
-var React = require('react');
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var Navigation = require('../../../navigation/conference/2017/navigation.jsx');
-var Footer = require('../../../footer/conference/2017/footer.jsx');
+const Navigation = require('../../../navigation/conference/2017/navigation.jsx');
+const Footer = require('../../../footer/conference/2017/footer.jsx');
 
 require('../page.scss');
 
-var Page = React.createClass({
-    type: 'Page',
-    render: function () {
-        return (
-            <div className="page mod-conference">
-                <div id="navigation">
-                    <Navigation />
-                </div>
-                <div id="view">
-                    {this.props.children}
-                </div>
-                <div id="footer">
-                    <Footer />
-                </div>
-            </div>
-        );
-    }
-});
+const Page = props => (
+    <div className="page mod-conference">
+        <div id="navigation">
+            <Navigation />
+        </div>
+        <div id="view">
+            {props.children}
+        </div>
+        <div id="footer">
+            <Footer />
+        </div>
+    </div>
+);
+
+Page.propTypes = {
+    children: PropTypes.node
+};
 
 module.exports = Page;
diff --git a/src/components/page/conference/2018/page.jsx b/src/components/page/conference/2018/page.jsx
index e8dd6e6f8..c4d242d31 100644
--- a/src/components/page/conference/2018/page.jsx
+++ b/src/components/page/conference/2018/page.jsx
@@ -1,27 +1,27 @@
-var React = require('react');
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var Navigation = require('../../../navigation/conference/2018/navigation.jsx');
-var Footer = require('../../../footer/conference/2018/footer.jsx');
+const Navigation = require('../../../navigation/conference/2018/navigation.jsx');
+const Footer = require('../../../footer/conference/2018/footer.jsx');
 
 require('../page.scss');
 
-var Page = React.createClass({
-    type: 'Page',
-    render: function () {
-        return (
-            <div className="page mod-conference">
-                <div id="navigation">
-                    <Navigation />
-                </div>
-                <div id="view">
-                    {this.props.children}
-                </div>
-                <div id="footer">
-                    <Footer />
-                </div>
-            </div>
-        );
-    }
-});
+const Page = props => (
+    <div className="page mod-conference">
+        <div id="navigation">
+            <Navigation />
+        </div>
+        <div id="view">
+            {props.children}
+        </div>
+        <div id="footer">
+            <Footer />
+        </div>
+    </div>
+);
+
+Page.propTypes = {
+    children: PropTypes.node
+};
 
 module.exports = Page;
diff --git a/src/components/page/www/page.jsx b/src/components/page/www/page.jsx
index 070f7e1dc..d286deb2b 100644
--- a/src/components/page/www/page.jsx
+++ b/src/components/page/www/page.jsx
@@ -1,29 +1,31 @@
-var React = require('react');
-var classNames = require('classnames');
+const classNames = require('classnames');
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var Navigation = require('../../navigation/www/navigation.jsx');
-var Footer = require('../../footer/www/footer.jsx');
+const Navigation = require('../../navigation/www/navigation.jsx');
+const Footer = require('../../footer/www/footer.jsx');
 
-var Page = React.createClass({
-    type: 'Page',
-    render: function () {
-        var classes = classNames({
-            'staging': process.env.SCRATCH_ENV == 'staging'
-        });
-        return (
-            <div className="page">
-                <div id="navigation" className={classes}>
-                    <Navigation />
-                </div>
-                <div id="view">
-                    {this.props.children}
-                </div>
-                <div id="footer">
-                    <Footer />
-                </div>
-            </div>
-        );
-    }
-});
+const Page = props => (
+    <div className="page">
+        <div
+            className={classNames({
+                staging: process.env.SCRATCH_ENV === 'staging'
+            })}
+            id="navigation"
+        >
+            <Navigation />
+        </div>
+        <div id="view">
+            {props.children}
+        </div>
+        <div id="footer">
+            <Footer />
+        </div>
+    </div>
+);
+
+Page.propTypes = {
+    children: PropTypes.node
+};
 
 module.exports = Page;
diff --git a/src/components/progression/progression.jsx b/src/components/progression/progression.jsx
index b4a7a4b56..e632b190a 100644
--- a/src/components/progression/progression.jsx
+++ b/src/components/progression/progression.jsx
@@ -1,42 +1,45 @@
-var classNames = require('classnames');
-var React = require('react');
+const classNames = require('classnames');
+const PropTypes = require('prop-types');
+const React = require('react');
 
-module.exports = React.createClass({
-    displayName: 'Progression',
-    propTypes: {
-        step: function (props, propName, componentName) {
-            var stepValidator = function (props, propName) {
-                if (props[propName] > -1 && props[propName] < props.children.length) {
-                    return null;
-                } else {
-                    return new Error('Prop `step` out of range');
+const Progression = props => {
+    const childProps = {
+        activeStep: props.step,
+        totalSteps: React.Children.count(props.children)
+    };
+    return (
+        <div
+            className={classNames('progression', props.className)}
+            {...props}
+        >
+            {React.Children.map(props.children, (child, id) => {
+                if (id === props.step) {
+                    return React.cloneElement(child, childProps);
                 }
-            };
-            return (
-                React.PropTypes.number.isRequired(props, propName, componentName) ||
-                stepValidator(props, propName, componentName)
-            );
-        }
-    },
-    getDefaultProps: function () {
-        return {
-            step: 0
-        };
-    },
-    render: function () {
-        var childProps = {
-            activeStep: this.props.step,
-            totalSteps: React.Children.count(this.props.children)
+            })}
+        </div>
+    );
+};
+
+Progression.propTypes = {
+    children: PropTypes.node,
+    className: PropTypes.string,
+    step: function (props, propName, componentName) {
+        const stepValidator = (propz, name) => {
+            if (propz[name] > -1 && propz[name] < propz.children.length) {
+                return null;
+            }
+            return new Error('Prop `step` out of range');
         };
         return (
-            <div {... this.props}
-                 className={classNames('progression', this.props.className)}>
-                {React.Children.map(this.props.children, function (child, id) {
-                    if (id === this.props.step) {
-                        return React.cloneElement(child, childProps);
-                    }
-                }, this)}
-            </div>
+            (typeof props[propName] === 'number' ? null : new Error('Not a number')) ||
+            stepValidator(props, propName, componentName)
         );
     }
-});
+};
+
+Progression.defaultProps = {
+    step: 0
+};
+
+module.exports = Progression;
diff --git a/src/components/registration/registration.jsx b/src/components/registration/registration.jsx
index f0dffa57a..e0dd7d94e 100644
--- a/src/components/registration/registration.jsx
+++ b/src/components/registration/registration.jsx
@@ -1,53 +1,62 @@
-var React = require('react');
-var IframeModal = require('../modal/iframe/modal.jsx');
+const bindAll = require('lodash.bindall');
+const PropTypes = require('prop-types');
+const React = require('react');
+
+const IframeModal = require('../modal/iframe/modal.jsx');
 
 require('./registration.scss');
 
-var Registration = React.createClass({
-    propTypes: {
-        isOpen: React.PropTypes.bool,
-        onRegistrationDone: React.PropTypes.func,
-        onRequestClose: React.PropTypes.func
-    },
-    onMessage: function (e) {
-        if (e.origin != window.location.origin) return;
-        if (e.source != this.registrationIframe.contentWindow) return;
-        if (e.data == 'registration-done') this.props.onRegistrationDone();
-        if (e.data == 'registration-relaunch') {
+class Registration extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleMessage',
+            'toggleMessageListener'
+        ]);
+    }
+    componentDidMount () {
+        if (this.props.isOpen) this.toggleMessageListener(true);
+    }
+    componentDidUpdate (prevProps) {
+        this.toggleMessageListener(this.props.isOpen && !prevProps.isOpen);
+    }
+    componentWillUnmount () {
+        this.toggleMessageListener(false);
+    }
+    handleMessage (e) {
+        if (e.origin !== window.location.origin) return;
+        if (e.source !== this.registrationIframe.contentWindow) return;
+        if (e.data === 'registration-done') this.props.handleRegistrationDone();
+        if (e.data === 'registration-relaunch') {
             this.registrationIframe.contentWindow.location.reload();
         }
-    },
-    toggleMessageListener: function (present) {
+    }
+    toggleMessageListener (present) {
         if (present) {
-            window.addEventListener('message', this.onMessage);
+            window.addEventListener('message', this.handleMessage);
         } else {
-            window.removeEventListener('message', this.onMessage);
+            window.removeEventListener('message', this.handleMessage);
         }
-    },
-    componentDidMount: function () {
-        if (this.props.isOpen) this.toggleMessageListener(true);
-    },
-    componentDidUpdate: function (prevProps) {
-        this.toggleMessageListener(this.props.isOpen && !prevProps.isOpen);
-    },
-    componentWillUnmount: function () {
-        this.toggleMessageListener(false);
-    },
-    render: function () {
+    }
+    render () {
         return (
             <IframeModal
-                isOpen={this.props.isOpen}
-                onRequestClose={this.props.onRequestClose}
                 className="mod-registration"
-                componentRef={
-                    function (iframe) {
-                        this.registrationIframe = iframe;
-                    }.bind(this)
-                }
+                componentRef={iframe => { // eslint-disable-line react/jsx-no-bind
+                    this.registrationIframe = iframe;
+                }}
+                isOpen={this.props.isOpen}
                 src="/accounts/standalone-registration/"
+                onRequestClose={this.props.handleRequestClose}
             />
         );
     }
-});
+}
+
+Registration.propTypes = {
+    handleRegistrationDone: PropTypes.func,
+    handleRequestClose: PropTypes.func,
+    isOpen: PropTypes.bool
+};
 
 module.exports = Registration;
diff --git a/src/components/registration/steps.jsx b/src/components/registration/steps.jsx
index 7e7ce68e4..3ce739a6b 100644
--- a/src/components/registration/steps.jsx
+++ b/src/components/registration/steps.jsx
@@ -1,1076 +1,1567 @@
-var React = require('react');
+const bindAll = require('lodash.bindall');
+const injectIntl = require('react-intl').injectIntl;
+const intlShape = require('react-intl').intlShape;
+const omit = require('lodash.omit');
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var api = require('../../lib/api');
-var countryData = require('../../lib/country-data');
-var intl = require('../../lib/intl.jsx');
+const api = require('../../lib/api');
+const countryData = require('../../lib/country-data');
+const intl = require('../../lib/intl.jsx');
 
-var Avatar = require('../../components/avatar/avatar.jsx');
-var Button = require('../../components/forms/button.jsx');
-var Card = require('../../components/card/card.jsx');
-var CharCount = require('../../components/forms/charcount.jsx');
-var Checkbox = require('../../components/forms/checkbox.jsx');
-var CheckboxGroup = require('../../components/forms/checkbox-group.jsx');
-var Form = require('../../components/forms/form.jsx');
-var GeneralError = require('../../components/forms/general-error.jsx');
-var Input = require('../../components/forms/input.jsx');
-var PhoneInput = require('../../components/forms/phone-input.jsx');
-var RadioGroup = require('../../components/forms/radio-group.jsx');
-var Select = require('../../components/forms/select.jsx');
-var Slide = require('../../components/slide/slide.jsx');
-var Spinner = require('../../components/spinner/spinner.jsx');
-var StepNavigation = require('../../components/stepnavigation/stepnavigation.jsx');
-var TextArea = require('../../components/forms/textarea.jsx');
-var Tooltip = require('../../components/tooltip/tooltip.jsx');
+const Avatar = require('../../components/avatar/avatar.jsx');
+const Button = require('../../components/forms/button.jsx');
+const Card = require('../../components/card/card.jsx');
+const CharCount = require('../../components/forms/charcount.jsx');
+const Checkbox = require('../../components/forms/checkbox.jsx');
+const CheckboxGroup = require('../../components/forms/checkbox-group.jsx');
+const Form = require('../../components/forms/form.jsx');
+const GeneralError = require('../../components/forms/general-error.jsx');
+const Input = require('../../components/forms/input.jsx');
+const PhoneInput = require('../../components/forms/phone-input.jsx');
+const RadioGroup = require('../../components/forms/radio-group.jsx');
+const Select = require('../../components/forms/select.jsx');
+const Slide = require('../../components/slide/slide.jsx');
+const Spinner = require('../../components/spinner/spinner.jsx');
+const StepNavigation = require('../../components/stepnavigation/stepnavigation.jsx');
+const TextArea = require('../../components/forms/textarea.jsx');
+const Tooltip = require('../../components/tooltip/tooltip.jsx');
 
 require('./steps.scss');
 
-var DEFAULT_COUNTRY = 'us';
+const DEFAULT_COUNTRY = 'us';
 
 /**
  * Return a list of options to give to frc select
- * @param  {Object} intl           react-intl, used to localize strings
- * @param  {String} defaultCountry optional string of default country to put at top of list
- * @return {Object}                ordered set of county options formatted for frc select
+ * @param  {object} reactIntl      react-intl, used to localize strings
+ * @param  {string} defaultCountry optional string of default country to put at top of list
+ * @return {object}                ordered set of county options formatted for frc select
  */
-var getCountryOptions = function (intl, defaultCountry) {
-    var options = countryData.countryOptions.concat({
-        label: intl.formatMessage({id: 'registration.selectCountry'}),
+const getCountryOptions = (reactIntl, defaultCountry) => {
+    const options = countryData.countryOptions.concat({
+        label: reactIntl.formatMessage({id: 'registration.selectCountry'}),
         disabled: true,
         selected: true
     });
+
     if (typeof defaultCountry !== 'undefined') {
-        return options.sort(function (a, b) {
+        return options.sort((a, b) => {
             if (a.disabled) return -1;
             if (b.disabled) return 1;
             if (a.value === defaultCountry) return -1;
             if (b.value === defaultCountry) return 1;
             return 0;
-        }.bind(this));
+        });
     }
     return options;
 };
 
-var NextStepButton = React.createClass({
-    getDefaultProps: function () {
-        return {
-            waiting: false,
-            text: 'Next Step'
-        };
-    },
-    render: function () {
-        return (
-            <Button type="submit" disabled={this.props.waiting} className="card-button" {... this.props}>
-                {this.props.waiting ?
-                    <Spinner /> :
-                    this.props.text
-                }
-            </Button>
-        );
-    }
-});
+const NextStepButton = props => (
+    <Button
+        className="card-button"
+        disabled={props.waiting}
+        type="submit"
+        {...omit(props, ['text', 'waiting'])}
+    >
+        {props.waiting ?
+            <Spinner /> :
+            props.text
+        }
+    </Button>
+);
 
-module.exports = {
-    UsernameStep: intl.injectIntl(React.createClass({
-        getDefaultProps: function () {
-            return {
-                showPassword: false,
-                waiting: false
-            };
-        },
-        getInitialState: function () {
-            return {
-                showPassword: this.props.showPassword,
-                waiting: false,
-                validUsername: ''
-            };
-        },
-        onChangeShowPassword: function (field, value) {
-            this.setState({showPassword: value});
-        },
-        validateUsername: function (username, callback) {
-            callback = callback || function () {};
-            if (!username) {
-                this.refs.form.refs.formsy.updateInputsWithError({
-                    'user.username': this.props.intl.formatMessage({id: 'form.validationRequired'})
+NextStepButton.propTypes = {
+    text: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
+    waiting: PropTypes.bool
+};
+
+NextStepButton.defaultProps = {
+    waiting: false,
+    text: 'Next Step'
+};
+
+
+/*
+ * USERNAME STEP
+ */
+class UsernameStep extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleChangeShowPassword',
+            'handleUsernameBlur',
+            'handleValidSubmit',
+            'validateUsername'
+        ]);
+        this.state = {
+            showPassword: props.showPassword,
+            waiting: false,
+            validUsername: ''
+        };
+    }
+    handleChangeShowPassword (field, value) {
+        this.setState({showPassword: value});
+    }
+    validateUsername (username, callback) {
+        callback = callback || function () {};
+
+        if (!username) {
+            this.form.formsy.updateInputsWithError({
+                'user.username': this.props.intl.formatMessage({id: 'form.validationRequired'})
+            });
+            return callback(false);
+        }
+
+        api({
+            host: '',
+            uri: `/accounts/check_username/${username}/`
+        }, (err, body, res) => {
+            if (err || res.statusCode !== 200) {
+                err = err || this.props.intl.formatMessage({id: 'general.error'});
+                this.form.formsy.updateInputsWithError({all: err});
+                return callback(false);
+            }
+            body = body[0];
+
+            switch (body.msg) {
+            case 'valid username':
+                this.setState({
+                    validUsername: 'pass'
+                });
+                return callback(true);
+            case 'username exists':
+                this.form.formsy.updateInputsWithError({
+                    'user.username': this.props.intl.formatMessage({
+                        id: 'registration.validationUsernameExists'
+                    })
+                });
+                return callback(false);
+            case 'bad username':
+                this.form.formsy.updateInputsWithError({
+                    'user.username': this.props.intl.formatMessage({
+                        id: 'registration.validationUsernameVulgar'
+                    })
+                });
+                return callback(false);
+            case 'invalid username':
+            default:
+                this.form.formsy.updateInputsWithError({
+                    'user.username': this.props.intl.formatMessage({
+                        id: 'registration.validationUsernameInvalid'
+                    })
                 });
                 return callback(false);
             }
-
-            api({
-                host: '',
-                uri: '/accounts/check_username/' + username + '/'
-            }, function (err, body, res) {
-                var formatMessage = this.props.intl.formatMessage;
-                if (err || res.statusCode !== 200) {
-                    err = err || formatMessage({id: 'general.error'});
-                    this.refs.form.refs.formsy.updateInputsWithError({all: err});
-                    return callback(false);
-                }
-                body = body[0];
-
-                switch (body.msg) {
-                case 'valid username':
-                    this.setState({
-                        validUsername: 'pass'
-                    });
-                    return callback(true);
-                case 'username exists':
-                    this.refs.form.refs.formsy.updateInputsWithError({
-                        'user.username': formatMessage({id: 'registration.validationUsernameExists'})
-                    });
-                    return callback(false);
-                case 'bad username':
-                    this.refs.form.refs.formsy.updateInputsWithError({
-                        'user.username': formatMessage({id: 'registration.validationUsernameVulgar'})
-                    });
-                    return callback(false);
-                case 'invalid username':
-                default:
-                    this.refs.form.refs.formsy.updateInputsWithError({
-                        'user.username': formatMessage({id: 'registration.validationUsernameInvalid'})
-                    });
-                    return callback(false);
-                }
-            }.bind(this));
-        },
-        onUsernameBlur: function (event) {
-            if (this.refs.form.refs.formsy.inputs[0].isValidValue(event.currentTarget.value)) {
-                this.validateUsername(event.currentTarget.value);
-            }
-        },
-        onValidSubmit: function (formData) {
-            this.setState({waiting: true});
-            this.validateUsername(formData.user.username, function (isValid) {
-                this.setState({waiting: false});
-                if (isValid) return this.props.onNextStep(formData);
-            }.bind(this));
-        },
-        render: function () {
-            var formatMessage = this.props.intl.formatMessage;
-            return (
-                <Slide className="registration-step username-step">
-                    <h2>
-                        {this.props.title ? (
-                            this.props.title
-                        ) : (
-                            <intl.FormattedMessage id="registration.usernameStepTitle" />
-                        )}
-                    </h2>
-                    <p className="description">
-                        {this.props.description ? (
-                            this.props.description
-                        ) : (
-                            <intl.FormattedMessage id="registration.usernameStepDescription" />
-                        )}
-                        {this.props.tooltip ? (
-                             <Tooltip title={'?'}
-                                 tipContent={this.props.tooltip} />
-                        ) : (
-                            null
-                        )}
-                    </p>
-                    <Card>
-                        <Form onValidSubmit={this.onValidSubmit} ref="form">
-                            <div>
-                                <div className="username-label">
-                                    <b>{formatMessage({id: 'registration.createUsername'})}</b>
-                                    {this.props.usernameHelp ? (
-                                        <p className="help-text">{this.props.usernameHelp}</p>
-                                    ):(
-                                        null
-                                    )}
-                                </div>
-                                <Input className={this.state.validUsername}
-                                       type="text"
-                                       name="user.username"
-                                       onBlur={this.onUsernameBlur}
-                                       validations={{
-                                           matchRegexp: /^[\w-]*$/,
-                                           minLength: 3,
-                                           maxLength: 20
-                                       }}
-                                       validationErrors={{
-                                           matchRegexp: formatMessage({
-                                               id: 'registration.validationUsernameRegexp'
-                                           }),
-                                           minLength: formatMessage({
-                                               id: 'registration.validationUsernameMinLength'
-                                           }),
-                                           maxLength: formatMessage({
-                                               id: 'registration.validationUsernameMaxLength'
-                                           })
-                                       }}
-                                       required />
+        });
+    }
+    handleUsernameBlur (event) {
+        if (this.form.formsy.inputs[0].isValidValue(event.currentTarget.value)) {
+            this.validateUsername(event.currentTarget.value);
+        }
+    }
+    handleValidSubmit (formData) {
+        this.setState({waiting: true});
+        this.validateUsername(formData.user.username, isValid => {
+            this.setState({waiting: false});
+            if (isValid) return this.props.onNextStep(formData);
+        });
+    }
+    render () {
+        return (
+            <Slide className="registration-step username-step">
+                <h2>
+                    {this.props.title ? (
+                        this.props.title
+                    ) : (
+                        <intl.FormattedMessage id="registration.usernameStepTitle" />
+                    )}
+                </h2>
+                <p className="description">
+                    {this.props.description ? (
+                        this.props.description
+                    ) : (
+                        <intl.FormattedMessage id="registration.usernameStepDescription" />
+                    )}
+                    {this.props.tooltip ? (
+                        <Tooltip
+                            tipContent={this.props.tooltip}
+                            title={'?'}
+                        />
+                    ) : (
+                        null
+                    )}
+                </p>
+                <Card>
+                    <Form
+                        ref={form => {
+                            this.form = form;
+                        }}
+                        onValidSubmit={this.handleValidSubmit}
+                    >
+                        <div>
+                            <div className="username-label">
+                                <b>
+                                    {this.props.intl.formatMessage({id: 'registration.createUsername'})}
+                                </b>
+                                {this.props.usernameHelp ? (
+                                    <p className="help-text">{this.props.usernameHelp}</p>
+                                ) : (
+                                    null
+                                )}
                             </div>
-                            <Input label={formatMessage({id: 'general.password'})}
-                                   type={this.state.showPassword ? 'text' : 'password'}
-                                   name="user.password"
-                                   validations={{
-                                       minLength: 6,
-                                       notEquals: 'password',
-                                       notEqualsField: 'user.username'
-                                   }}
-                                   validationErrors={{
-                                       minLength: formatMessage({
-                                           id: 'registration.validationPasswordLength'
-                                       }),
-                                       notEquals: formatMessage({
-                                           id: 'registration.validationPasswordNotEquals'
-                                       }),
-                                       notEqualsField: formatMessage({
-                                           id: 'registration.validationPasswordNotUsername'
-                                       })
-                                   }}
-                                   required />
-                            <Checkbox label={formatMessage({id: 'registration.showPassword'})}
-                                      value={this.state.showPassword}
-                                      onChange={this.onChangeShowPassword}
-                                      help={null}
-                                      name="showPassword" />
-                            <GeneralError name="all" />
-                            <NextStepButton waiting={this.props.waiting || this.state.waiting}
-                                            text={<intl.FormattedMessage id="registration.nextStep" />} />
-                        </Form>
-                    </Card>
-                    <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
-                </Slide>
-            );
-        }
-    })),
-    ChoosePasswordStep: intl.injectIntl(React.createClass({
-        getDefaultProps: function () {
-            return {
-                username: null,
-                showPassword: false,
-                waiting: false
-            };
-        },
-        getInitialState: function () {
-            return {
-                showPassword: this.props.showPassword
-            };
-        },
-        onChangeShowPassword: function (field, value) {
-            this.setState({showPassword: value});
-        },
-        render: function () {
-            var formatMessage = this.props.intl.formatMessage;
-            return (
-                <Slide className="registration-step choose-password-step">
-                    <h2>{formatMessage({id: 'registration.choosePasswordStepTitle'})}</h2>
-                    <p className="description">
-                        <intl.FormattedMessage id="registration.choosePasswordStepDescription" />
-                        <Tooltip title={'?'}
-                                 tipContent={formatMessage({id: 'registration.choosePasswordStepTooltip'})} />
-                    </p>
-
-                    <Card>
-                        <Form onValidSubmit={this.props.onNextStep}>
-                            <Input label={formatMessage({id: 'registration.newPassword'})}
-                                   type={this.state.showPassword ? 'text' : 'password'}
-                                   name="user.password"
-                                   validations={{
-                                       minLength: 6,
-                                       notEquals: 'password',
-                                       notEqualsUsername: this.props.username
-                                   }}
-                                   validationErrors={{
-                                       minLength: formatMessage({
-                                           id: 'registration.validationPasswordLength'
-                                       }),
-                                       notEquals: formatMessage({
-                                           id: 'registration.validationPasswordNotEquals'
-                                       }),
-                                       notEqualsUsername: formatMessage({
-                                           id: 'registration.validationPasswordNotUsername'
-                                       })
-                                   }}
-                                   required />
-                            <Checkbox label={formatMessage({id: 'registration.showPassword'})}
-                                      value={this.state.showPassword}
-                                      onChange={this.onChangeShowPassword}
-                                      help={null}
-                                      name="showPassword" />
-                            <NextStepButton waiting={this.props.waiting || this.state.waiting}
-                                            text={<intl.FormattedMessage id="registration.nextStep" />} />
-                        </Form>
-                    </Card>
-                    <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
-                </Slide>
-            );
-        }
-    })),
-    DemographicsStep: intl.injectIntl(React.createClass({
-        getDefaultProps: function () {
-            return {
-                waiting: false,
-                description: null,
-                birthOffset: 0
-            };
-        },
-        getInitialState: function () {
-            return {otherDisabled: true};
-        },
-        getMonthOptions: function () {
-            return [
-                'January', 'February', 'March', 'April', 'May', 'June', 'July',
-                'August', 'September', 'October', 'November', 'December'
-            ].map(function (label, id) {
-                return {
-                    value: id + 1,
-                    label: this.props.intl.formatMessage({id: 'general.month' + label})};
-            }.bind(this));
-        },
-        getYearOptions: function () {
-            return Array.apply(null, Array(100)).map(function (v, id) {
-                var year = new Date().getFullYear() - (id + this.props.birthOffset);
-                return {value: year, label: year};
-            }.bind(this));
-        },
-        onChooseGender: function (name, gender) {
-            this.setState({otherDisabled: gender !== 'other'});
-        },
-        onValidSubmit: function (formData, reset, invalidate) {
-            var birthdate = new Date(
-              formData.user.birth.year,
-              formData.user.birth.month - 1,
-              1
-            );
-            if (((Date.now() - birthdate) / (24*3600*1000*365.25)) < this.props.birthOffset) {
-                return invalidate({
-                    'user.birth.year': this.props.intl.formatMessage({id: 'teacherRegistration.validationAge'})
-                });
-            }
-            return this.props.onNextStep(formData);
-        },
-        render: function () {
-            var formatMessage = this.props.intl.formatMessage;
-            return (
-                <Slide className="registration-step demographics-step">
-                    <h2>
-                        <intl.FormattedMessage id="registration.personalStepTitle" />
-                    </h2>
-                    <p className="description">
-                        {this.props.description ?
-                            this.props.description
-                        :
-                            <intl.FormattedMessage id="registration.personalStepDescription" />
-                        }
-                        <Tooltip title={'?'}
-                                 tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
-                    </p>
-                    <Card>
-                        <Form onValidSubmit={this.onValidSubmit}>
-                            <Select label={formatMessage({id: 'general.birthMonth'})}
-                                    name="user.birth.month"
-                                    options={this.getMonthOptions()}
-                                    required />
-                            <Select label={formatMessage({id: 'general.birthYear'})}
-                                    name="user.birth.year"
-                                    options={this.getYearOptions()} required />
-                            <RadioGroup label={formatMessage({id: 'general.gender'})}
-                                        name="user.gender"
-                                        onChange={this.onChooseGender}
-                                        options={[
-                                            {value: 'female', label: formatMessage({id: 'general.female'})},
-                                            {value: 'male', label: formatMessage({id: 'general.male'})},
-                                            {value: 'other', label: <Input
-                                                className="demographics-step-input-other"
-                                                name="user.genderOther"
-                                                type="text"
-                                                validations={{
-                                                    maxLength: 25
-                                                }}
-                                                validationErrors={{
-                                                    maxLength: formatMessage({
-                                                        id: 'registration.validationMaxLength'
-                                                    })
-                                                }}
-                                                disabled={this.state.otherDisabled}
-                                                required={!this.state.otherDisabled}
-                                                help={null}
-                                            />}
-                                        ]}
-                                        required />
-                            <Select label={formatMessage({id: 'general.country'})}
-                                    name="user.country"
-                                    options={getCountryOptions(this.props.intl, DEFAULT_COUNTRY)}
-                                    required />
-                            <Checkbox className="demographics-checkbox-is-robot"
-                                      label="I'm a robot!"
-                                      name="user.isRobot" />
-                            <NextStepButton waiting={this.props.waiting}
-                                            text={<intl.FormattedMessage id="registration.nextStep" />} />
-                        </Form>
-                    </Card>
-                    <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
-                </Slide>
-            );
-        }
-    })),
-    NameStep: intl.injectIntl(React.createClass({
-        getDefaultProps: function () {
-            return {
-                waiting: false
-            };
-        },
-        render: function () {
-            var formatMessage = this.props.intl.formatMessage;
-            return (
-                <Slide className="registration-step name-step">
-                    <h2>
-                        <intl.FormattedHTMLMessage id="teacherRegistration.nameStepTitle" />
-                    </h2>
-                    <p className="description">
-                        <intl.FormattedMessage id="teacherRegistration.nameStepDescription" />
-                        <Tooltip title={'?'}
-                                 tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
-                    </p>
-                    <Card>
-                        <Form onValidSubmit={this.props.onNextStep}>
-                            <Input label={formatMessage({id: 'teacherRegistration.firstName'})}
-                                   type="text"
-                                   name="user.name.first"
-                                   validations={{
-                                       maxLength: 50
-                                   }}
-                                   validationErrors={{
-                                       maxLength: formatMessage({
-                                           id: 'registration.validationMaxLength'
-                                       })
-                                   }}
-                                   required />
-                            <Input label={formatMessage({id: 'teacherRegistration.lastName'})}
-                                   type="text"
-                                   name="user.name.last"
-                                   validations={{
-                                       maxLength: 50
-                                   }}
-                                   validationErrors={{
-                                       maxLength: formatMessage({
-                                           id: 'registration.validationMaxLength'
-                                       })
-                                   }}
-                                   required />
-                            <NextStepButton waiting={this.props.waiting}
-                                            text={<intl.FormattedMessage id="registration.nextStep" />} />
-                        </Form>
-                    </Card>
-                    <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
-                </Slide>
-            );
-        }
-    })),
-    PhoneNumberStep: intl.injectIntl(React.createClass({
-        getDefaultProps: function () {
-            return {
-                defaultCountry: DEFAULT_COUNTRY,
-                waiting: false
-            };
-        },
-        onValidSubmit: function (formData, reset, invalidate) {
-            if (!formData.phone || formData.phone.national_number === '+') {
-                return invalidate({
-                    'phone': this.props.intl.formatMessage({id: 'form.validationRequired'})
-                });
-            }
-            return this.props.onNextStep(formData);
-        },
-        render: function () {
-            var formatMessage = this.props.intl.formatMessage;
-            return (
-                <Slide className="registration-step phone-step">
-                    <h2>
-                        <intl.FormattedMessage id="teacherRegistration.phoneNumber" />
-                    </h2>
-                    <p className="description">
-                        <intl.FormattedMessage id="teacherRegistration.phoneStepDescription" />
-                        <Tooltip title={'?'}
-                                 tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
-                    </p>
-                    <Card>
-                        <Form onValidSubmit={this.onValidSubmit}>
-                            <PhoneInput label={formatMessage({id: 'teacherRegistration.phoneNumber'})}
-                                        name="phone"
-                                        defaultCountry={this.props.defaultCountry}
-                                        required />
-                            <Checkbox label={formatMessage({id: 'teacherRegistration.phoneConsent'})}
-                                      name="phoneConsent"
-                                      required="isFalse"
-                                      validationErrors={{
-                                          isFalse: formatMessage({id: 'teacherRegistration.validationPhoneConsent'})
-                                      }} />
-                            <NextStepButton waiting={this.props.waiting}
-                                            text={<intl.FormattedMessage id="registration.nextStep" />} />
-                        </Form>
-                    </Card>
-                    <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
-                </Slide>
-            );
-        }
-    })),
-    OrganizationStep: intl.injectIntl(React.createClass({
-        getInitialState: function () {
-            return {
-                otherDisabled: true
-            };
-        },
-        getDefaultProps: function () {
-            return {
-                waiting: false
-            };
-        },
-        organizationL10nStems: [
-            'orgChoiceElementarySchool',
-            'orgChoiceMiddleSchool',
-            'orgChoiceHighSchool',
-            'orgChoiceUniversity',
-            'orgChoiceAfterschool',
-            'orgChoiceMuseum',
-            'orgChoiceLibrary',
-            'orgChoiceCamp'
-        ],
-        getOrganizationOptions: function () {
-            var options = this.organizationL10nStems.map(function (choice, id) {
-                return {
-                    value: id,
-                    label: this.props.intl.formatMessage({
-                        id: 'teacherRegistration.' + choice
-                    })
-                };
-            }.bind(this));
-            
-            // Add "Other" option with empty string, since input field is used
-            var otherId = options.length;
-            options.push({value: otherId, label: ' '});
-            
-            return options;
-        },
-        onChooseOrganization: function (name, values) {
-            this.setState({otherDisabled: values.indexOf(this.organizationL10nStems.length) === -1});
-        },
-        render: function () {
-            var formatMessage = this.props.intl.formatMessage;
-            return (
-                <Slide className="registration-step organization-step">
-                    <h2>
-                        <intl.FormattedMessage id="teacherRegistration.organization" />
-                    </h2>
-                    <p className="description">
-                        <intl.FormattedMessage id="teacherRegistration.orgStepDescription" />
-                        <Tooltip title={'?'}
-                                 tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
-                    </p>
-                    <Card>
-                        <Form onValidSubmit={this.props.onNextStep}>
-                            <Input label={formatMessage({id: 'teacherRegistration.organization'})}
-                                   type="text"
-                                   name="organization.name"
-                                   validations={{
-                                       maxLength: 50
-                                   }}
-                                   validationErrors={{
-                                       maxLength: formatMessage({
-                                           id: 'registration.validationMaxLength'
-                                       })
-                                   }}
-                                   required />
-                            <Input label={formatMessage({id: 'teacherRegistration.orgTitle'})}
-                                   type="text"
-                                   name="organization.title"
-                                   validations={{
-                                       maxLength: 50
-                                   }}
-                                   validationErrors={{
-                                       maxLength: formatMessage({
-                                           id: 'registration.validationMaxLength'
-                                       })
-                                   }}
-                                   required />
-                            <div className="organization-type">
-                                <b><intl.FormattedMessage id="teacherRegistration.orgType" /></b>
-                                <p className="help-text">
-                                    <intl.FormattedMessage id="teacherRegistration.checkAll" />
-                                </p>
-                                <CheckboxGroup name="organization.type"
-                                               value={[]}
-                                               options={this.getOrganizationOptions()}
-                                               onChange={this.onChooseOrganization}
-                                               validations={{
-                                                   minLength: 1
-                                               }}
-                                               validationErrors={{
-                                                   minLength: formatMessage({
-                                                       id: 'form.validationRequired'
-                                                   })
-                                               }}
-                                               required />
-                            </div>
-                            <div className="other-input">
-                                <Input name="organization.other"
-                                       type="text"
-                                       validations={{
-                                           maxLength: 50
-                                       }}
-                                       validationErrors={{
-                                           maxLength: formatMessage({
-                                               id: 'registration.validationMaxLength'
-                                           })
-                                       }}
-                                       disabled={this.state.otherDisabled}
-                                       required={!this.state.otherDisabled}
-                                       help={null}
-                                       placeholder={formatMessage({id: 'general.other'})} />
-                            </div>
-                            <div className="url-input">
-                                <b><intl.FormattedMessage id="general.website" /></b>
-                                <p className="help-text">
-                                    <intl.FormattedMessage id="teacherRegistration.notRequired" />
-                                </p>
-                                <Input type="url"
-                                       name="organization.url"
-                                       validations={{
-                                           maxLength: 200
-                                       }}
-                                       validationErrors={{
-                                           maxLength: formatMessage({
-                                               id: 'registration.validationMaxLength'
-                                           })
-                                       }}
-                                       required="isFalse"
-                                       placeholder={'http://'} />
-                            </div>
-                            <NextStepButton waiting={this.props.waiting}
-                                            text={<intl.FormattedMessage id="registration.nextStep" />} />
-                        </Form>
-                    </Card>
-                    <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
-                </Slide>
-            );
-        }
-    })),
-    AddressStep: intl.injectIntl(React.createClass({
-        getDefaultProps: function () {
-            return {
-                defaultCountry: DEFAULT_COUNTRY,
-                waiting: false
-            };
-        },
-        getInitialState: function () {
-            return {
-                countryChoice: this.props.defaultCountry,
-                waiting: false
-            };
-        },
-        onChangeCountry: function (field, choice) {
-            this.setState({countryChoice: choice});
-        },
-        render: function () {
-            var formatMessage = this.props.intl.formatMessage;
-            var stateOptions = countryData.subdivisionOptions[this.state.countryChoice];
-            stateOptions = [{}].concat(stateOptions);
-            return (
-                <Slide className="registration-step address-step">
-                    <h2>
-                        <intl.FormattedMessage id="teacherRegistration.addressStepTitle" />
-                    </h2>
-                    <p className="description">
-                        <intl.FormattedMessage id="teacherRegistration.addressStepDescription" />
-                        <Tooltip title={'?'}
-                                 tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
-                    </p>
-                    <Card>
-                        <Form onValidSubmit={this.props.onNextStep}>
-                            <Select label={formatMessage({id: 'general.country'})}
-                                    name="address.country"
-                                    options={getCountryOptions(this.props.intl)}
-                                    value={this.props.defaultCountry}
-                                    onChange={this.onChangeCountry}
-                                    required />
-                            <Input label={formatMessage({id: 'teacherRegistration.addressLine1'})}
-                                   type="text"
-                                   name="address.line1"
-                                   validations={{
-                                       maxLength: 100
-                                   }}
-                                   validationErrors={{
-                                       maxLength: formatMessage({
-                                           id: 'registration.validationMaxLength'
-                                       })
-                                   }}
-                                   required />
-                            <Input label={formatMessage({id: 'teacherRegistration.addressLine2'})}
-                                   type="text"
-                                   name="address.line2"
-                                   validations={{
-                                       maxLength: 100
-                                   }}
-                                   validationErrors={{
-                                       maxLength: formatMessage({
-                                           id: 'registration.validationMaxLength'
-                                       })
-                                   }}
-                                   required="isFalse" />
-                            <Input label={formatMessage({id: 'teacherRegistration.city'})}
-                                   type="text"
-                                   name="address.city"
-                                   validations={{
-                                       maxLength: 50
-                                   }}
-                                   validationErrors={{
-                                       maxLength: formatMessage({
-                                           id: 'registration.validationMaxLength'
-                                       })
-                                   }}
-                                   required />
-                            {stateOptions.length > 2 ?
-                                <Select label={formatMessage({id: 'teacherRegistration.stateProvince'})}
-                                        name="address.state"
-                                        options={stateOptions}
-                                        required /> :
-                                []
-                            }
-                            <b className="row-label">
-                                <intl.FormattedMessage id="teacherRegistration.zipCode" />
-                            </b>
-                            {this.state.countryChoice !== 'us' ?
-                                <p className="help-text">
-                                    <intl.FormattedMessage id="teacherRegistration.notRequired" />
-                                </p> : []
-                            }
-                            <Input type="text"
-                                   name="address.zip"
-                                   validations={{
-                                       maxLength: 10
-                                   }}
-                                   validationErrors={{
-                                       maxLength: formatMessage({
-                                           id: 'registration.validationMaxLength'
-                                       })
-                                   }}
-                                   required={(this.state.countryChoice === 'us') ? true : 'isFalse'} />
-                            <GeneralError name="all" />
-                            <NextStepButton waiting={this.props.waiting || this.state.waiting}
-                                            text={<intl.FormattedMessage id="registration.nextStep" />} />
-                        </Form>
-                    </Card>
-                    <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
-                </Slide>
-            );
-        }
-    })),
-    UseScratchStep: intl.injectIntl(React.createClass({
-        getDefaultProps: function () {
-            return {
-                waiting: false,
-                maxCharacters: 300
-            };
-        },
-        getInitialState: function () {
-            return {
-                characterCount: 0
-            };
-        },
-        handleTyping: function (name, value) {
-            this.setState({
-                characterCount: value.length
-            });
-        },
-        render: function () {
-            var formatMessage = this.props.intl.formatMessage;
-            var textAreaClass = (this.state.characterCount > this.props.maxCharacters) ? 'fail' : '';
-
-            return (
-                <Slide className="registration-step usescratch-step">
-                    <h2>
-                        <intl.FormattedMessage id="teacherRegistration.useScratchStepTitle" />
-                    </h2>
-                    <p className="description">
-                        <intl.FormattedMessage id="teacherRegistration.useScratchStepDescription" />
-                        <Tooltip title={'?'}
-                                 tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
-                    </p>
-                    <Card>
-                        <Form onValidSubmit={this.props.onNextStep}>
-                            <TextArea label={formatMessage({id: 'teacherRegistration.howUseScratch'})}
-                                      name="useScratch"
-                                      className={textAreaClass}
-                                      onChange={this.handleTyping}
-                                      validations={{
-                                          maxLength: this.props.maxCharacters
-                                      }}
-                                      validationErrors={{
-                                          maxLength: formatMessage({
-                                              id: 'teacherRegistration.useScratchMaxLength'
-                                          })
-                                      }}
-                                      required />
-                            <CharCount maxCharacters={this.props.maxCharacters}
-                                       currentCharacters={this.state.characterCount} />
-                            <NextStepButton waiting={this.props.waiting}
-                                            text={<intl.FormattedMessage id="registration.nextStep" />} />
-                        </Form>
-                    </Card>
-                    <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
-                </Slide>
-            );
-        }
-    })),
-    EmailStep: intl.injectIntl(React.createClass({
-        getDefaultProps: function () {
-            return {
-                waiting: false
-            };
-        },
-        getInitialState: function () {
-            return {
-                waiting: false
-            };
-        },
-        onValidSubmit: function (formData, reset, invalidate) {
-            this.setState({waiting: true});
-            api({
-                host: '',
-                uri: '/accounts/check_email/',
-                params: {email: formData.user.email}
-            }, function (err, res) {
-                this.setState({waiting: false});
-                if (err) return invalidate({all: err});
-                res = res[0];
-                switch (res.msg) {
-                case 'valid email':
-                    return this.props.onNextStep(formData);
-                default:
-                    return invalidate({'user.email': res.msg});
-                }
-            }.bind(this));
-        },
-        render: function () {
-            var formatMessage = this.props.intl.formatMessage;
-            return (
-                <Slide className="registration-step email-step">
-                    <h2>
-                        <intl.FormattedMessage id="teacherRegistration.emailStepTitle" />
-                    </h2>
-                    <p className="description">
-                        <intl.FormattedMessage id="teacherRegistration.emailStepDescription" />
-                        <Tooltip title={'?'}
-                                 tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
-                    </p>
-                    <Card>
-                        <Form onValidSubmit={this.onValidSubmit}>
                             <Input
-                                label={formatMessage({id: 'general.emailAddress'})}
-                                type="text"
-                                name="user.email"
-                                validations="isEmail"
-                                validationError={formatMessage({id: 'general.validationEmail'})}
                                 required
-                            />
-                            <Input
-                                label={formatMessage({id: 'general.confirmEmail'})}
+                                className={this.state.validUsername}
+                                name="user.username"
                                 type="text"
-                                name="confirmEmail"
-                                validations="equalsField:user.email"
                                 validationErrors={{
-                                    equalsField: formatMessage({id: 'general.validationEmailMatch'})
+                                    matchRegexp: this.props.intl.formatMessage({
+                                        id: 'registration.validationUsernameRegexp'
+                                    }),
+                                    minLength: this.props.intl.formatMessage({
+                                        id: 'registration.validationUsernameMinLength'
+                                    }),
+                                    maxLength: this.props.intl.formatMessage({
+                                        id: 'registration.validationUsernameMaxLength'
+                                    })
                                 }}
-                                required
+                                validations={{
+                                    matchRegexp: /^[\w-]*$/,
+                                    minLength: 3,
+                                    maxLength: 20
+                                }}
+                                onBlur={this.handleUsernameBlur}
                             />
-                            <Checkbox
-                                label={formatMessage({id: 'registration.optIn'})}
-                                value={true}
-                                help={null}
-                                name="subscribe"
-                            />
-                            <GeneralError name="all" />
-                            <NextStepButton
-                                waiting={this.props.waiting}
-                                text={<intl.FormattedMessage id="registration.nextStep" />}
-                            />
-                        </Form>
-                    </Card>
-                    <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
-                </Slide>
-            );
-        }
-    })),
-    TeacherApprovalStep: intl.injectIntl(React.createClass({
-        getDefaultProps: function () {
-            return {
-                email: null,
-                invited: false,
-                confirmed: false
-            };
-        },
-        render: function () {
-            return (
-                <Slide className="registration-step last-step">
-                    <h2>
-                        <intl.FormattedMessage id="registration.lastStepTitle" />
-                    </h2>
-                    <p className="description">
-                        <intl.FormattedMessage id="registration.lastStepDescription" />
-                    </p>
-                    {this.props.confirmed || !this.props.email ?
-                        []
-                        :
-                        (<Card className="confirm">
-                            <h4><intl.FormattedMessage id="registration.confirmYourEmail" /></h4>
-                            <p>
-                                <intl.FormattedMessage id="registration.confirmYourEmailDescription" /><br />
-                                <strong>{this.props.email}</strong>
-                            </p>
-                        </Card>)
-                    }
-                    {this.props.invited ?
-                        <Card className="wait">
-                            <h4><intl.FormattedMessage id="registration.waitForApproval" /></h4>
-                            <p>
-                                <intl.FormattedMessage id="registration.waitForApprovalDescription" />
-                            </p>
-                        </Card>
-                        :
-                        []
-                    }
-                    <Card className="resources">
-                        <h4><intl.FormattedMessage id="registration.checkOutResources" /></h4>
-                        <p>
-                            <intl.FormattedHTMLMessage id="registration.checkOutResourcesDescription" />
-                        </p>
-                    </Card>
-                </Slide>
-            );
-        }
-    })),
-    ClassInviteNewStudentStep: intl.injectIntl(React.createClass({
-        getDefaultProps: function () {
-            return {
-                waiting: false
-            };
-        },
-        onNextStep: function () {
-            this.props.onNextStep();
-        },
-        render: function () {
-            var formatMessage = this.props.intl.formatMessage;
-            return (
-                <Slide className="registration-step class-invite-step">
-                    {this.props.waiting ? [
-                        <Spinner />
-                    ] : [
-                        <Avatar className="invite-avatar"
-                                src={this.props.classroom.educator.profile.images['50x50']} />,
-                        <h2>{this.props.classroom.educator.username}</h2>,
-                        <p className="description">
-                            {formatMessage({id: 'registration.classroomInviteNewStudentStepDescription'})}
-                        </p>,
-                        <Card>
-                            <div className="contents">
-                                <h3>{this.props.classroom.title}</h3>
-                                <img className="class-image" src={this.props.classroom.images['250x150']} />
-                            </div>
-                            <NextStepButton onClick={this.onNextStep}
-                                            waiting={this.props.waiting}
-                                            text={formatMessage({id: 'general.getStarted'})} />
-                        </Card>,
-                        <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
-                    ]}
-                </Slide>
-            );
-        }
-    })),
-    ClassInviteExistingStudentStep: intl.injectIntl(React.createClass({
-        getDefaultProps: function () {
-            return {
-                classroom: null,
-                onHandleLogOut: function () {},
-                studentUsername: null,
-                waiting: false
-            };
-        },
-        onNextStep: function () {
-            this.props.onNextStep();
-        },
-        render: function () {
-            var formatMessage = this.props.intl.formatMessage;
-            return (
-                <Slide className="registration-step class-invite-step">
-                    {this.props.waiting ? [
-                        <Spinner />
-                    ] : [
-                        <h2>{this.props.studentUsername}</h2>,
-                        <p className="description">
-                            {formatMessage({id: 'registration.classroomInviteExistingStudentStepDescription'})}
-                        </p>,
-                        <Card>
-                            <div className="contents">
-                                <h3>{this.props.classroom.title}</h3>
-                                <img className="class-image" src={this.props.classroom.images['250x150']} />
-                                <p>{formatMessage({id: 'registration.invitedBy'})}</p>
-                                <p><strong>{this.props.classroom.educator.username}</strong></p>
-                            </div>
-                            <NextStepButton onClick={this.onNextStep}
-                                            waiting={this.props.waiting}
-                                            text={formatMessage({id: 'general.getStarted'})} />
-                        </Card>,
-                        <p><a onClick={this.props.onHandleLogOut}>{formatMessage({id: 'registration.notYou'})}</a></p>,
-                        <StepNavigation steps={this.props.totalSteps - 1} active={this.props.activeStep} />
-                    ]}
-                </Slide>
-            );
-        }
-    })),
-    ClassWelcomeStep: intl.injectIntl(React.createClass({
-        getDefaultProps: function () {
-            return {
-                waiting: false
-            };
-        },
-        onNextStep: function () {
-            this.props.onNextStep();
-        },
-        render: function () {
-            var formatMessage = this.props.intl.formatMessage;
-            return (
-                <Slide className="registration-step class-welcome-step">
-                    {this.props.waiting ? [
-                        <Spinner />
-                    ] : [
-                        <h2>{formatMessage({id: 'registration.welcomeStepTitle'})}</h2>,
-                        <p className="description">{formatMessage({id: 'registration.welcomeStepDescription'})}</p>,
-                        <Card>
-                            {this.props.classroom ? (
-                                <div className="contents">
-                                    <h3>{this.props.classroom.title}</h3>
-                                    <img className="class-image" src={this.props.classroom.images['250x150']} />
-                                    <p>{formatMessage({id: 'registration.welcomeStepPrompt'})}</p>
-                                </div>
-                            ) : (
-                                null
-                            )}
-                            <NextStepButton onClick={this.onNextStep}
-                                            waiting={this.props.waiting}
-                                            text={formatMessage({id: 'registration.goToClass'})} />
-                        </Card>
-                    ]}
-                </Slide>
-            );
-        }
-    })),
-    RegistrationError: intl.injectIntl(React.createClass({
-        render: function () {
-            return (
-                <Slide className="registration-step error-step">
-                    <h2>Something went wrong</h2>
-                    <Card>
-                        <h4>There was an error while processing your registration</h4>
-                        <p>
-                            {this.props.children}
-                        </p>
-                    </Card>
-                </Slide>
-            );
-        }
-    }))
+                        </div>
+                        <Input
+                            required
+                            label={
+                                this.props.intl.formatMessage({id: 'general.password'})
+                            }
+                            name="user.password"
+                            type={this.state.showPassword ? 'text' : 'password'}
+                            validationErrors={{
+                                minLength: this.props.intl.formatMessage({
+                                    id: 'registration.validationPasswordLength'
+                                }),
+                                notEquals: this.props.intl.formatMessage({
+                                    id: 'registration.validationPasswordNotEquals'
+                                }),
+                                notEqualsField: this.props.intl.formatMessage({
+                                    id: 'registration.validationPasswordNotUsername'
+                                })
+                            }}
+                            validations={{
+                                minLength: 6,
+                                notEquals: 'password',
+                                notEqualsField: 'user.username'
+                            }}
+                        />
+                        <Checkbox
+                            help={null}
+                            label={
+                                this.props.intl.formatMessage({id: 'registration.showPassword'})
+                            }
+                            name="showPassword"
+                            value={this.state.showPassword}
+                            onChange={this.handleChangeShowPassword}
+                        />
+                        <GeneralError name="all" />
+                        <NextStepButton
+                            text={<intl.FormattedMessage id="registration.nextStep" />}
+                            waiting={this.props.waiting || this.state.waiting}
+                        />
+                    </Form>
+                </Card>
+                <StepNavigation
+                    active={this.props.activeStep}
+                    steps={this.props.totalSteps - 1}
+                />
+            </Slide>
+        );
+    }
+}
+
+UsernameStep.propTypes = {
+    activeStep: PropTypes.number,
+    description: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
+    intl: intl.intlShape,
+    onNextStep: PropTypes.func,
+    showPassword: PropTypes.bool,
+    title: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
+    tooltip: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
+    totalSteps: PropTypes.number,
+    usernameHelp: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
+    waiting: PropTypes.bool
 };
+
+UsernameStep.defaultProps = {
+    showPassword: false,
+    waiting: false
+};
+
+const IntlUsernameStep = injectIntl(UsernameStep);
+
+
+/*
+ * PASSWORD STEP
+ */
+class ChoosePasswordStep extends React.Component { // eslint-disable-line react/no-multi-comp
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleChangeShowPassword'
+        ]);
+        this.state = {
+            showPassword: props.showPassword
+        };
+    }
+    handleChangeShowPassword (field, value) {
+        this.setState({showPassword: value});
+    }
+    render () {
+        return (
+            <Slide className="registration-step choose-password-step">
+                <h2>
+                    {this.props.intl.formatMessage({id: 'registration.choosePasswordStepTitle'})}
+                </h2>
+                <p className="description">
+                    <intl.FormattedMessage id="registration.choosePasswordStepDescription" />
+                    <Tooltip
+                        tipContent={
+                            this.props.intl.formatMessage({id: 'registration.choosePasswordStepTooltip'})
+                        }
+                        title={'?'}
+                    />
+                </p>
+
+                <Card>
+                    <Form onValidSubmit={this.props.onNextStep}>
+                        <Input
+                            required
+                            label={
+                                this.props.intl.formatMessage({id: 'registration.newPassword'})
+                            }
+                            name="user.password"
+                            type={this.state.showPassword ? 'text' : 'password'}
+                            validationErrors={{
+                                minLength: this.props.intl.formatMessage({
+                                    id: 'registration.validationPasswordLength'
+                                }),
+                                notEquals: this.props.intl.formatMessage({
+                                    id: 'registration.validationPasswordNotEquals'
+                                }),
+                                notEqualsUsername: this.props.intl.formatMessage({
+                                    id: 'registration.validationPasswordNotUsername'
+                                })
+                            }}
+                            validations={{
+                                minLength: 6,
+                                notEquals: 'password',
+                                notEqualsUsername: this.props.username
+                            }}
+                        />
+                        <Checkbox
+                            help={null}
+                            label={
+                                this.props.intl.formatMessage({id: 'registration.showPassword'})
+                            }
+                            name="showPassword"
+                            value={this.state.showPassword}
+                            onChange={this.handleChangeShowPassword}
+                        />
+                        <NextStepButton
+                            text={<intl.FormattedMessage id="registration.nextStep" />}
+                            waiting={this.props.waiting || this.state.waiting}
+                        />
+                    </Form>
+                </Card>
+                <StepNavigation
+                    active={this.props.activeStep}
+                    steps={this.props.totalSteps - 1}
+                />
+            </Slide>
+        );
+    }
+}
+
+ChoosePasswordStep.propTypes = {
+    activeStep: PropTypes.number,
+    intl: intlShape,
+    onNextStep: PropTypes.func,
+    showPassword: PropTypes.bool,
+    totalSteps: PropTypes.number,
+    username: PropTypes.string,
+    waiting: PropTypes.bool
+};
+
+ChoosePasswordStep.defaultProps = {
+    showPassword: false,
+    username: null,
+    waiting: false
+};
+
+const IntlChoosePasswordStep = injectIntl(ChoosePasswordStep);
+
+
+/*
+ * DEMOGRAPHICS STEP
+ */
+class DemographicsStep extends React.Component { // eslint-disable-line react/no-multi-comp
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'getMonthOptions',
+            'getYearOptions',
+            'handleChooseGender',
+            'handleValidSubmit'
+        ]);
+        this.state = {
+            otherDisabled: true
+        };
+    }
+    getMonthOptions () {
+        return [
+            'January', 'February', 'March', 'April', 'May', 'June', 'July',
+            'August', 'September', 'October', 'November', 'December'
+        ].map((label, id) => ({
+            value: id + 1,
+            label: this.props.intl.formatMessage({id: `general.month${label}`})
+        }));
+    }
+    getYearOptions () {
+        return Array.apply(null, Array(100)).map((v, id) => {
+            const year = new Date().getFullYear() - (id + this.props.birthOffset);
+            return {value: year, label: year};
+        });
+    }
+    handleChooseGender (name, gender) {
+        this.setState({otherDisabled: gender !== 'other'});
+    }
+    handleValidSubmit (formData, reset, invalidate) {
+        const birthdate = new Date(
+            formData.user.birth.year,
+            formData.user.birth.month - 1,
+            1
+        );
+        if (((Date.now() - birthdate) / (24 * 3600 * 1000 * 365.25)) < this.props.birthOffset) {
+            return invalidate({
+                'user.birth.year': this.props.intl.formatMessage({id: 'teacherRegistration.validationAge'})
+            });
+        }
+        return this.props.onNextStep(formData);
+    }
+    render () {
+        return (
+            <Slide className="registration-step demographics-step">
+                <h2>
+                    <intl.FormattedMessage id="registration.personalStepTitle" />
+                </h2>
+                <p className="description">
+                    {this.props.description ?
+                        this.props.description :
+                        <intl.FormattedMessage id="registration.personalStepDescription" />
+                    }
+                    <Tooltip
+                        tipContent={
+                            this.props.intl.formatMessage({id: 'registration.nameStepTooltip'})
+                        }
+                        title={'?'}
+                    />
+                </p>
+                <Card>
+                    <Form onValidSubmit={this.handleValidSubmit}>
+                        <Select
+                            required
+                            label={
+                                this.props.intl.formatMessage({id: 'general.birthMonth'})
+                            }
+                            name="user.birth.month"
+                            options={this.getMonthOptions()}
+                        />
+                        <Select
+                            required
+                            label={
+                                this.props.intl.formatMessage({id: 'general.birthYear'})
+                            }
+                            name="user.birth.year"
+                            options={this.getYearOptions()}
+                        />
+                        <RadioGroup
+                            required
+                            label={
+                                this.props.intl.formatMessage({id: 'general.gender'})
+                            }
+                            name="user.gender"
+                            options={[
+                                {
+                                    value: 'female',
+                                    label: this.props.intl.formatMessage({id: 'general.female'})
+                                },
+                                {
+                                    value: 'male',
+                                    label: this.props.intl.formatMessage({id: 'general.male'})
+                                },
+                                {
+                                    value: 'other',
+                                    label: <Input
+                                        className="demographics-step-input-other"
+                                        disabled={this.state.otherDisabled}
+                                        help={null}
+                                        name="user.genderOther"
+                                        required={!this.state.otherDisabled}
+                                        type="text"
+                                        validationErrors={{
+                                            maxLength: this.props.intl.formatMessage({
+                                                id: 'registration.validationMaxLength'
+                                            })
+                                        }}
+                                        validations={{
+                                            maxLength: 25
+                                        }}
+                                    />
+                                }
+                            ]}
+                            onChange={this.handleChooseGender}
+                        />
+                        <Select
+                            required
+                            label={this.props.intl.formatMessage({id: 'general.country'})}
+                            name="user.country"
+                            options={getCountryOptions(this.props.intl, DEFAULT_COUNTRY)}
+                        />
+                        <Checkbox
+                            className="demographics-checkbox-is-robot"
+                            label="I'm a robot!"
+                            name="user.isRobot"
+                        />
+                        <NextStepButton
+                            text={<intl.FormattedMessage id="registration.nextStep" />}
+                            waiting={this.props.waiting}
+                        />
+                    </Form>
+                </Card>
+                <StepNavigation
+                    active={this.props.activeStep}
+                    steps={this.props.totalSteps - 1}
+                />
+            </Slide>
+        );
+    }
+}
+
+DemographicsStep.propTypes = {
+    activeStep: PropTypes.number,
+    birthOffset: PropTypes.number,
+    description: PropTypes.string,
+    intl: intlShape,
+    onNextStep: PropTypes.func,
+    totalSteps: PropTypes.number,
+    waiting: PropTypes.bool
+};
+
+DemographicsStep.defaultProps = {
+    waiting: false,
+    description: null,
+    birthOffset: 0
+};
+
+const IntlDemographicsStep = injectIntl(DemographicsStep);
+
+
+/*
+ * NAME STEP
+ */
+const NameStep = props => (
+    <Slide className="registration-step name-step">
+        <h2>
+            <intl.FormattedHTMLMessage id="teacherRegistration.nameStepTitle" />
+        </h2>
+        <p className="description">
+            <intl.FormattedMessage id="teacherRegistration.nameStepDescription" />
+            <Tooltip
+                tipContent={
+                    props.intl.formatMessage({id: 'registration.nameStepTooltip'})
+                }
+                title={'?'}
+            />
+        </p>
+        <Card>
+            <Form onValidSubmit={props.onNextStep}>
+                <Input
+                    required
+                    label={
+                        props.intl.formatMessage({id: 'teacherRegistration.firstName'})
+                    }
+                    name="user.name.first"
+                    type="text"
+                    validationErrors={{
+                        maxLength: props.intl.formatMessage({
+                            id: 'registration.validationMaxLength'
+                        })
+                    }}
+                    validations={{
+                        maxLength: 50
+                    }}
+                />
+                <Input
+                    required
+                    label={
+                        props.intl.formatMessage({id: 'teacherRegistration.lastName'})
+                    }
+                    name="user.name.last"
+                    type="text"
+                    validationErrors={{
+                        maxLength: props.intl.formatMessage({
+                            id: 'registration.validationMaxLength'
+                        })
+                    }}
+                    validations={{
+                        maxLength: 50
+                    }}
+                />
+                <NextStepButton
+                    text={<intl.FormattedMessage id="registration.nextStep" />}
+                    waiting={props.waiting}
+                />
+            </Form>
+        </Card>
+        <StepNavigation
+            active={props.activeStep}
+            steps={props.totalSteps - 1}
+        />
+    </Slide>
+);
+
+NameStep.propTypes = {
+    activeStep: PropTypes.number,
+    intl: intlShape,
+    onNextStep: PropTypes.func,
+    totalSteps: PropTypes.number,
+    waiting: PropTypes.bool
+};
+
+NameStep.defaultProps = {
+    waiting: false
+};
+
+const IntlNameStep = injectIntl(NameStep);
+
+
+/*
+ * PHONE NUMBER STEP
+ */
+class PhoneNumberStep extends React.Component { // eslint-disable-line react/no-multi-comp
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleValidSubmit'
+        ]);
+    }
+    handleValidSubmit (formData, reset, invalidate) {
+        if (!formData.phone || formData.phone.national_number === '+') {
+            return invalidate({
+                phone: this.props.intl.formatMessage({id: 'form.validationRequired'})
+            });
+        }
+        return this.props.onNextStep(formData);
+    }
+    render () {
+        return (
+            <Slide className="registration-step phone-step">
+                <h2>
+                    <intl.FormattedMessage id="teacherRegistration.phoneNumber" />
+                </h2>
+                <p className="description">
+                    <intl.FormattedMessage id="teacherRegistration.phoneStepDescription" />
+                    <Tooltip
+                        tipContent={
+                            this.props.intl.formatMessage({id: 'registration.nameStepTooltip'})
+                        }
+                        title={'?'}
+                    />
+                </p>
+                <Card>
+                    <Form onValidSubmit={this.handleValidSubmit}>
+                        <PhoneInput
+                            required
+                            defaultCountry={this.props.defaultCountry}
+                            label={
+                                this.props.intl.formatMessage({id: 'teacherRegistration.phoneNumber'})
+                            }
+                            name="phone"
+                        />
+                        <Checkbox
+                            label={
+                                this.props.intl.formatMessage({id: 'teacherRegistration.phoneConsent'})
+                            }
+                            name="phoneConsent"
+                            required="isFalse"
+                            validationErrors={{
+                                isFalse: this.props.intl.formatMessage({
+                                    id: 'teacherRegistration.validationPhoneConsent'
+                                })
+                            }}
+                        />
+                        <NextStepButton
+                            text={<intl.FormattedMessage id="registration.nextStep" />}
+                            waiting={this.props.waiting}
+                        />
+                    </Form>
+                </Card>
+                <StepNavigation
+                    active={this.props.activeStep}
+                    steps={this.props.totalSteps - 1}
+                />
+            </Slide>
+        );
+    }
+}
+
+PhoneNumberStep.propTypes = {
+    activeStep: PropTypes.number,
+    defaultCountry: PropTypes.string,
+    intl: intlShape,
+    onNextStep: PropTypes.func,
+    totalSteps: PropTypes.number,
+    waiting: PropTypes.bool
+};
+
+PhoneNumberStep.defaultProps = {
+    defaultCountry: DEFAULT_COUNTRY,
+    waiting: false
+};
+
+const IntlPhoneNumberStep = injectIntl(PhoneNumberStep);
+
+
+/*
+ * ORGANIZATION STEP
+ */
+const ORGANIZATION_L10N_STEMS = [
+    'orgChoiceElementarySchool',
+    'orgChoiceMiddleSchool',
+    'orgChoiceHighSchool',
+    'orgChoiceUniversity',
+    'orgChoiceAfterschool',
+    'orgChoiceMuseum',
+    'orgChoiceLibrary',
+    'orgChoiceCamp'
+];
+
+class OrganizationStep extends React.Component { // eslint-disable-line react/no-multi-comp
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'getOrganizationOptions',
+            'handleChooseOrganization'
+        ]);
+        this.state = {
+            otherDisabled: true
+        };
+    }
+    getOrganizationOptions () {
+        const options = ORGANIZATION_L10N_STEMS.map((choice, id) => ({
+            value: id,
+            label: this.props.intl.formatMessage({
+                id: `teacherRegistration.${choice}`
+            })
+        }));
+        
+        // Add "Other" option with empty string, since input field is used
+        const otherId = options.length;
+        options.push({value: otherId, label: ' '});
+        
+        return options;
+    }
+    handleChooseOrganization (name, values) {
+        this.setState({
+            otherDisabled: values.indexOf(ORGANIZATION_L10N_STEMS.length) === -1
+        });
+    }
+    render () {
+        return (
+            <Slide className="registration-step organization-step">
+                <h2>
+                    <intl.FormattedMessage id="teacherRegistration.organization" />
+                </h2>
+                <p className="description">
+                    <intl.FormattedMessage id="teacherRegistration.orgStepDescription" />
+                    <Tooltip
+                        tipContent={
+                            this.props.intl.formatMessage({id: 'registration.nameStepTooltip'})
+                        }
+                        title={'?'}
+                    />
+                </p>
+                <Card>
+                    <Form onValidSubmit={this.props.onNextStep}>
+                        <Input
+                            required
+                            label={
+                                this.props.intl.formatMessage({id: 'teacherRegistration.organization'})
+                            }
+                            name="organization.name"
+                            type="text"
+                            validationErrors={{
+                                maxLength: this.props.intl.formatMessage({
+                                    id: 'registration.validationMaxLength'
+                                })
+                            }}
+                            validations={{
+                                maxLength: 50
+                            }}
+                        />
+                        <Input
+                            required
+                            label={this.props.intl.formatMessage({id: 'teacherRegistration.orgTitle'})}
+                            name="organization.title"
+                            type="text"
+                            validationErrors={{
+                                maxLength: this.props.intl.formatMessage({
+                                    id: 'registration.validationMaxLength'
+                                })
+                            }}
+                            validations={{
+                                maxLength: 50
+                            }}
+                        />
+                        <div className="organization-type">
+                            <b><intl.FormattedMessage id="teacherRegistration.orgType" /></b>
+                            <p className="help-text">
+                                <intl.FormattedMessage id="teacherRegistration.checkAll" />
+                            </p>
+                            <CheckboxGroup
+                                required
+                                name="organization.type"
+                                options={this.getOrganizationOptions()}
+                                validationErrors={{
+                                    minLength: this.props.intl.formatMessage({
+                                        id: 'form.validationRequired'
+                                    })
+                                }}
+                                validations={{
+                                    minLength: 1
+                                }}
+                                value={[]}
+                                onChange={this.handleChooseOrganization}
+                            />
+                        </div>
+                        <div className="other-input">
+                            <Input
+                                disabled={this.state.otherDisabled}
+                                help={null}
+                                name="organization.other"
+                                placeholder={
+                                    this.props.intl.formatMessage({id: 'general.other'})
+                                }
+                                required={!this.state.otherDisabled}
+                                type="text"
+                                validationErrors={{
+                                    maxLength: this.props.intl.formatMessage({
+                                        id: 'registration.validationMaxLength'
+                                    })
+                                }}
+                                validations={{
+                                    maxLength: 50
+                                }}
+                            />
+                        </div>
+                        <div className="url-input">
+                            <b><intl.FormattedMessage id="general.website" /></b>
+                            <p className="help-text">
+                                <intl.FormattedMessage id="teacherRegistration.notRequired" />
+                            </p>
+                            <Input
+                                name="organization.url"
+                                placeholder={'http://'}
+                                required="isFalse"
+                                type="url"
+                                validationErrors={{
+                                    maxLength: this.props.intl.formatMessage({
+                                        id: 'registration.validationMaxLength'
+                                    })
+                                }}
+                                validations={{
+                                    maxLength: 200
+                                }}
+                            />
+                        </div>
+                        <NextStepButton
+                            text={<intl.FormattedMessage id="registration.nextStep" />}
+                            waiting={this.props.waiting}
+                        />
+                    </Form>
+                </Card>
+                <StepNavigation
+                    active={this.props.activeStep}
+                    steps={this.props.totalSteps - 1}
+                />
+            </Slide>
+        );
+    }
+}
+
+OrganizationStep.propTypes = {
+    activeStep: PropTypes.number,
+    intl: intlShape,
+    onNextStep: PropTypes.func,
+    totalSteps: PropTypes.number,
+    waiting: PropTypes.bool
+};
+
+OrganizationStep.defaultProps = {
+    waiting: false
+};
+
+const IntlOrganizationStep = injectIntl(OrganizationStep);
+
+
+/*
+ * ADDRESS STEP
+ */
+class AddressStep extends React.Component { // eslint-disable-line react/no-multi-comp
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleChangeCountry'
+        ]);
+        this.state = {
+            countryChoice: props.defaultCountry,
+            waiting: false
+        };
+    }
+    handleChangeCountry (field, choice) {
+        this.setState({countryChoice: choice});
+    }
+    render () {
+        let stateOptions = countryData.subdivisionOptions[this.state.countryChoice];
+        stateOptions = [{}].concat(stateOptions);
+        return (
+            <Slide className="registration-step address-step">
+                <h2>
+                    <intl.FormattedMessage id="teacherRegistration.addressStepTitle" />
+                </h2>
+                <p className="description">
+                    <intl.FormattedMessage id="teacherRegistration.addressStepDescription" />
+                    <Tooltip
+                        tipContent={
+                            this.props.intl.formatMessage({id: 'registration.nameStepTooltip'})
+                        }
+                        title={'?'}
+                    />
+                </p>
+                <Card>
+                    <Form onValidSubmit={this.props.onNextStep}>
+                        <Select
+                            required
+                            label={
+                                this.props.intl.formatMessage({id: 'general.country'})
+                            }
+                            name="address.country"
+                            options={getCountryOptions(this.props.intl)}
+                            value={this.props.defaultCountry}
+                            onChange={this.handleChangeCountry}
+                        />
+                        <Input
+                            required
+                            label={
+                                this.props.intl.formatMessage({id: 'teacherRegistration.addressLine1'})
+                            }
+                            name="address.line1"
+                            type="text"
+                            validationErrors={{
+                                maxLength: this.props.intl.formatMessage({
+                                    id: 'registration.validationMaxLength'
+                                })
+                            }}
+                            validations={{
+                                maxLength: 100
+                            }}
+                        />
+                        <Input
+                            label={
+                                this.props.intl.formatMessage({id: 'teacherRegistration.addressLine2'})
+                            }
+                            name="address.line2"
+                            required="isFalse"
+                            type="text"
+                            validationErrors={{
+                                maxLength: this.props.intl.formatMessage({
+                                    id: 'registration.validationMaxLength'
+                                })
+                            }}
+                            validations={{
+                                maxLength: 100
+                            }}
+                        />
+                        <Input
+                            required
+                            label={
+                                this.props.intl.formatMessage({id: 'teacherRegistration.city'})
+                            }
+                            name="address.city"
+                            type="text"
+                            validationErrors={{
+                                maxLength: this.props.intl.formatMessage({
+                                    id: 'registration.validationMaxLength'
+                                })
+                            }}
+                            validations={{
+                                maxLength: 50
+                            }}
+                        />
+                        {stateOptions.length > 2 ?
+                            <Select
+                                required
+                                label={
+                                    this.props.intl.formatMessage({id: 'teacherRegistration.stateProvince'})
+                                }
+                                name="address.state"
+                                options={stateOptions}
+                            /> : []
+                        }
+                        <b className="row-label">
+                            <intl.FormattedMessage id="teacherRegistration.zipCode" />
+                        </b>
+                        {this.state.countryChoice === 'us' ? [] : <p className="help-text">
+                            <intl.FormattedMessage id="teacherRegistration.notRequired" />
+                        </p>}
+                        <Input
+                            name="address.zip"
+                            required={(this.state.countryChoice === 'us') ? true : 'isFalse'}
+                            type="text"
+                            validationErrors={{
+                                maxLength: this.props.intl.formatMessage({
+                                    id: 'registration.validationMaxLength'
+                                })
+                            }}
+                            validations={{
+                                maxLength: 10
+                            }}
+                        />
+                        <GeneralError name="all" />
+                        <NextStepButton
+                            text={<intl.FormattedMessage id="registration.nextStep" />}
+                            waiting={this.props.waiting || this.state.waiting}
+                        />
+                    </Form>
+                </Card>
+                <StepNavigation
+                    active={this.props.activeStep}
+                    steps={this.props.totalSteps - 1}
+                />
+            </Slide>
+        );
+    }
+}
+
+AddressStep.propTypes = {
+    activeStep: PropTypes.number,
+    defaultCountry: PropTypes.string,
+    intl: intlShape,
+    onNextStep: PropTypes.func,
+    totalSteps: PropTypes.number,
+    waiting: PropTypes.bool
+};
+
+AddressStep.defaultProps = {
+    defaultCountry: DEFAULT_COUNTRY,
+    waiting: false
+};
+
+const IntlAddressStep = injectIntl(AddressStep);
+
+
+/*
+ * USE SCRATCH STEP
+ */
+class UseScratchStep extends React.Component { // eslint-disable-line react/no-multi-comp
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleTyping'
+        ]);
+        this.state = {
+            characterCount: 0
+        };
+    }
+    handleTyping (name, value) {
+        this.setState({
+            characterCount: value.length
+        });
+    }
+    render () {
+        const textAreaClass = (this.state.characterCount > this.props.maxCharacters) ? 'fail' : '';
+        return (
+            <Slide className="registration-step usescratch-step">
+                <h2>
+                    <intl.FormattedMessage id="teacherRegistration.useScratchStepTitle" />
+                </h2>
+                <p className="description">
+                    <intl.FormattedMessage id="teacherRegistration.useScratchStepDescription" />
+                    <Tooltip
+                        tipContent={
+                            this.props.intl.formatMessage({id: 'registration.nameStepTooltip'})
+                        }
+                        title={'?'}
+                    />
+                </p>
+                <Card>
+                    <Form onValidSubmit={this.props.onNextStep}>
+                        <TextArea
+                            required
+                            className={textAreaClass}
+                            label={
+                                this.props.intl.formatMessage({id: 'teacherRegistration.howUseScratch'})
+                            }
+                            name="useScratch"
+                            validationErrors={{
+                                maxLength: this.props.intl.formatMessage({
+                                    id: 'teacherRegistration.useScratchMaxLength'
+                                })
+                            }}
+                            validations={{
+                                maxLength: this.props.maxCharacters
+                            }}
+                            onChange={this.handleTyping}
+                        />
+                        <CharCount
+                            currentCharacters={this.state.characterCount}
+                            maxCharacters={this.props.maxCharacters}
+                        />
+                        <NextStepButton
+                            text={<intl.FormattedMessage id="registration.nextStep" />}
+                            waiting={this.props.waiting}
+                        />
+                    </Form>
+                </Card>
+                <StepNavigation
+                    active={this.props.activeStep}
+                    steps={this.props.totalSteps - 1}
+                />
+            </Slide>
+        );
+    }
+}
+
+UseScratchStep.propTypes = {
+    activeStep: PropTypes.number,
+    intl: intlShape,
+    maxCharacters: PropTypes.number,
+    onNextStep: PropTypes.func,
+    totalSteps: PropTypes.number,
+    waiting: PropTypes.bool
+};
+
+UseScratchStep.defaultProps = {
+    maxCharacters: 300,
+    waiting: false
+};
+
+const IntlUseScratchStep = injectIntl(UseScratchStep);
+
+
+/*
+ * EMAIL STEP
+ */
+class EmailStep extends React.Component { // eslint-disable-line react/no-multi-comp
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleValidSubmit'
+        ]);
+        this.state = {
+            waiting: false
+        };
+    }
+    handleValidSubmit (formData, reset, invalidate) {
+        this.setState({waiting: true});
+        api({
+            host: '',
+            uri: '/accounts/check_email/',
+            params: {email: formData.user.email}
+        }, (err, res) => {
+            this.setState({
+                waiting: false
+            });
+
+            if (err) return invalidate({all: err});
+            res = res[0];
+            switch (res.msg) {
+            case 'valid email':
+                return this.props.onNextStep(formData);
+            default:
+                return invalidate({'user.email': res.msg});
+            }
+        });
+    }
+    render () {
+        return (
+            <Slide className="registration-step email-step">
+                <h2>
+                    <intl.FormattedMessage id="teacherRegistration.emailStepTitle" />
+                </h2>
+                <p className="description">
+                    <intl.FormattedMessage id="teacherRegistration.emailStepDescription" />
+                    <Tooltip
+                        tipContent={
+                            this.props.intl.formatMessage({id: 'registration.nameStepTooltip'})
+                        }
+                        title={'?'}
+                    />
+                </p>
+                <Card>
+                    <Form onValidSubmit={this.handleValidSubmit}>
+                        <Input
+                            required
+                            label={
+                                this.props.intl.formatMessage({id: 'general.emailAddress'})
+                            }
+                            name="user.email"
+                            type="text"
+                            validationError={
+                                this.props.intl.formatMessage({id: 'general.validationEmail'})
+                            }
+                            validations="isEmail"
+                        />
+                        <Input
+                            required
+                            label={this.props.intl.formatMessage({id: 'general.confirmEmail'})}
+                            name="confirmEmail"
+                            type="text"
+                            validationErrors={{
+                                equalsField: this.props.intl.formatMessage({id: 'general.validationEmailMatch'})
+                            }}
+                            validations="equalsField:user.email"
+                        />
+                        <Checkbox
+                            value
+                            help={null}
+                            label={
+                                this.props.intl.formatMessage({id: 'registration.optIn'})
+                            }
+                            name="subscribe"
+                        />
+                        <GeneralError name="all" />
+                        <NextStepButton
+                            text={<intl.FormattedMessage id="registration.nextStep" />}
+                            waiting={this.props.waiting}
+                        />
+                    </Form>
+                </Card>
+                <StepNavigation
+                    active={this.props.activeStep}
+                    steps={this.props.totalSteps - 1}
+                />
+            </Slide>
+        );
+    }
+}
+
+EmailStep.propTypes = {
+    activeStep: PropTypes.number,
+    intl: intlShape,
+    onNextStep: PropTypes.func,
+    totalSteps: PropTypes.number,
+    waiting: PropTypes.bool
+};
+
+EmailStep.defaultProps = {
+    waiting: false
+};
+
+const IntlEmailStep = injectIntl(EmailStep);
+
+
+/*
+ * TEACHER APPROVAL STEP
+ */
+const TeacherApprovalStep = props => (
+    <Slide className="registration-step last-step">
+        <h2>
+            <intl.FormattedMessage id="registration.lastStepTitle" />
+        </h2>
+        <p className="description">
+            <intl.FormattedMessage id="registration.lastStepDescription" />
+        </p>
+        {props.confirmed || !props.email ?
+            [] : (
+                <Card className="confirm">
+                    <h4><intl.FormattedMessage id="registration.confirmYourEmail" /></h4>
+                    <p>
+                        <intl.FormattedMessage id="registration.confirmYourEmailDescription" /><br />
+                        <strong>{props.email}</strong>
+                    </p>
+                </Card>
+            )
+        }
+        {props.invited ?
+            <Card className="wait">
+                <h4><intl.FormattedMessage id="registration.waitForApproval" /></h4>
+                <p>
+                    <intl.FormattedMessage id="registration.waitForApprovalDescription" />
+                </p>
+            </Card> : []
+        }
+        <Card className="resources">
+            <h4><intl.FormattedMessage id="registration.checkOutResources" /></h4>
+            <p>
+                <intl.FormattedHTMLMessage id="registration.checkOutResourcesDescription" />
+            </p>
+        </Card>
+    </Slide>
+);
+
+TeacherApprovalStep.propTypes = {
+    confirmed: PropTypes.bool,
+    email: PropTypes.string,
+    invited: PropTypes.bool
+};
+
+TeacherApprovalStep.defaultProps = {
+    confirmed: false,
+    email: null,
+    invited: false
+};
+
+const IntlTeacherApprovalStep = injectIntl(TeacherApprovalStep);
+
+
+/*
+ * CLASS INVITE NEW STUDENT STEP
+ */
+const ClassInviteNewStudentStep = props => (
+    <Slide className="registration-step class-invite-step">
+        {props.waiting ? [
+            <Spinner key="spinner" />
+        ] : [
+            <Avatar
+                className="invite-avatar"
+                key="avatar"
+                src={props.classroom.educator.profile.images['50x50']}
+            />,
+            <h2 key="username">{props.classroom.educator.username}</h2>,
+            <p
+                className="description"
+                key="description"
+            >
+                {props.intl.formatMessage({id: 'registration.classroomInviteNewStudentStepDescription'})}
+            </p>,
+            <Card key="card">
+                <div className="contents">
+                    <h3>{props.classroom.title}</h3>
+                    <img
+                        className="class-image"
+                        src={props.classroom.images['250x150']}
+                    />
+                </div>
+                <NextStepButton
+                    text={props.intl.formatMessage({id: 'general.getStarted'})}
+                    waiting={props.waiting}
+                    onClick={props.onNextStep}
+                />
+            </Card>,
+            <StepNavigation
+                active={props.activeStep}
+                key="step"
+                steps={props.totalSteps - 1}
+            />
+        ]}
+    </Slide>
+);
+
+ClassInviteNewStudentStep.propTypes = {
+    activeStep: PropTypes.number,
+    classroom: PropTypes.shape({
+        educator: PropTypes.shape({
+            profile: PropTypes.object,
+            username: PropTypes.string
+        }),
+        images: PropTypes.object,
+        title: PropTypes.string
+    }),
+    intl: intlShape,
+    onNextStep: PropTypes.func,
+    totalSteps: PropTypes.number,
+    waiting: PropTypes.bool
+};
+
+ClassInviteNewStudentStep.defaultProps = {
+    waiting: false
+};
+
+const IntlClassInviteNewStudentStep = injectIntl(ClassInviteNewStudentStep);
+
+
+/*
+ * CLASS INVITE EXISTING STUDENT STEP
+ */
+const ClassInviteExistingStudentStep = props => (
+    <Slide className="registration-step class-invite-step">
+        {props.waiting ? [
+            <Spinner key="spinner" />
+        ] : [
+            <h2 key="username">{props.studentUsername}</h2>,
+            <p
+                className="description"
+                key="description"
+            >
+                {props.intl.formatMessage({id: 'registration.classroomInviteExistingStudentStepDescription'})}
+            </p>,
+            <Card key="card">
+                <div className="contents">
+                    <h3>{props.classroom.title}</h3>
+                    <img
+                        className="class-image"
+                        src={props.classroom.images['250x150']}
+                    />
+                    <p>{props.intl.formatMessage({id: 'registration.invitedBy'})}</p>
+                    <p><strong>{props.classroom.educator.username}</strong></p>
+                </div>
+                <NextStepButton
+                    text={props.intl.formatMessage({id: 'general.getStarted'})}
+                    waiting={props.waiting}
+                    onClick={props.onNextStep}
+                />
+            </Card>,
+            <p key="logout">
+                <a onClick={props.onHandleLogOut}>
+                    {props.intl.formatMessage({id: 'registration.notYou'})}
+                </a>
+            </p>,
+            <StepNavigation
+                active={props.activeStep}
+                key="step"
+                steps={props.totalSteps - 1}
+            />
+        ]}
+    </Slide>
+);
+
+ClassInviteExistingStudentStep.propTypes = {
+    activeStep: PropTypes.number,
+    classroom: PropTypes.shape({
+        educator: PropTypes.shape({
+            profile: PropTypes.object,
+            username: PropTypes.string
+        }),
+        images: PropTypes.object,
+        title: PropTypes.string
+    }),
+    intl: intlShape,
+    onHandleLogOut: PropTypes.func,
+    onNextStep: PropTypes.func,
+    studentUsername: PropTypes.string,
+    totalSteps: PropTypes.number,
+    waiting: PropTypes.bool
+};
+
+ClassInviteExistingStudentStep.defaultProps = {
+    classroom: null,
+    onHandleLogOut: () => {},
+    studentUsername: null,
+    waiting: false
+};
+
+const IntlClassInviteExistingStudentStep = injectIntl(ClassInviteExistingStudentStep);
+
+
+/*
+ * CLASS WELCOME STEP
+ */
+const ClassWelcomeStep = props => (
+    <Slide className="registration-step class-welcome-step">
+        {props.waiting ? [
+            <Spinner key="spinner" />
+        ] : [
+            <h2 key="title">
+                {props.intl.formatMessage({id: 'registration.welcomeStepTitle'})}
+            </h2>,
+            <p
+                className="description"
+                key="description"
+            >
+                {props.intl.formatMessage({id: 'registration.welcomeStepDescription'})}
+            </p>,
+            <Card key="card">
+                {props.classroom ? (
+                    <div className="contents">
+                        <h3>{props.classroom.title}</h3>
+                        <img
+                            className="class-image"
+                            src={props.classroom.images['250x150']}
+                        />
+                        <p>{props.intl.formatMessage({id: 'registration.welcomeStepPrompt'})}</p>
+                    </div>
+                ) : (
+                    null
+                )}
+                <NextStepButton
+                    text={
+                        props.intl.formatMessage({id: 'registration.goToClass'})
+                    }
+                    waiting={props.waiting}
+                    onClick={props.onNextStep}
+                />
+            </Card>
+        ]}
+    </Slide>
+);
+
+ClassWelcomeStep.propTypes = {
+    classroom: PropTypes.shape({
+        educator: PropTypes.shape({
+            profile: PropTypes.object,
+            username: PropTypes.string
+        }),
+        images: PropTypes.object,
+        title: PropTypes.string
+    }),
+    intl: intlShape,
+    onNextStep: PropTypes.func,
+    waiting: PropTypes.bool
+};
+
+ClassWelcomeStep.defaultProps = {
+    waiting: false
+};
+
+const IntlClassWelcomeStep = injectIntl(ClassWelcomeStep);
+
+
+/*
+ * REGISTRATION ERROR STEP
+ */
+const RegistrationError = props => (
+    <Slide className="registration-step error-step">
+        <h2>Something went wrong</h2>
+        <Card>
+            <h4>There was an error while processing your registration</h4>
+            <p>
+                {props.children}
+            </p>
+        </Card>
+    </Slide>
+);
+
+RegistrationError.propTypes = {
+    children: PropTypes.node
+};
+
+const IntlRegistrationError = injectIntl(RegistrationError);
+
+module.exports.UsernameStep = IntlUsernameStep;
+module.exports.ChoosePasswordStep = IntlChoosePasswordStep;
+module.exports.DemographicsStep = IntlDemographicsStep;
+module.exports.NameStep = IntlNameStep;
+module.exports.PhoneNumberStep = IntlPhoneNumberStep;
+module.exports.OrganizationStep = IntlOrganizationStep;
+module.exports.AddressStep = IntlAddressStep;
+module.exports.UseScratchStep = IntlUseScratchStep;
+module.exports.EmailStep = IntlEmailStep;
+module.exports.TeacherApprovalStep = IntlTeacherApprovalStep;
+module.exports.ClassInviteNewStudentStep = IntlClassInviteNewStudentStep;
+module.exports.ClassInviteExistingStudentStep = IntlClassInviteExistingStudentStep;
+module.exports.ClassWelcomeStep = IntlClassWelcomeStep;
+module.exports.RegistrationError = IntlRegistrationError;
diff --git a/src/components/slide/slide.jsx b/src/components/slide/slide.jsx
index cc297e33a..a63c2a07a 100644
--- a/src/components/slide/slide.jsx
+++ b/src/components/slide/slide.jsx
@@ -1,17 +1,18 @@
-var classNames = require('classnames');
-var React = require('react');
+const classNames = require('classnames');
+const PropTypes = require('prop-types');
+const React = require('react');
 
 require('./slide.scss');
 
-var Slide = React.createClass({
-    displayName: 'Slide',
-    render: function () {
-        return (
-            <div className={classNames(['slide', this.props.className])}>
-                {this.props.children}
-            </div>
-        );
-    }
-});
+const Slide = props => (
+    <div className={classNames(['slide', props.className])}>
+        {props.children}
+    </div>
+);
+
+Slide.propTypes = {
+    children: PropTypes.node,
+    className: PropTypes.string
+};
 
 module.exports = Slide;
diff --git a/src/components/social-message/social-message.jsx b/src/components/social-message/social-message.jsx
index d231e9563..5bb893494 100644
--- a/src/components/social-message/social-message.jsx
+++ b/src/components/social-message/social-message.jsx
@@ -1,57 +1,47 @@
-var classNames = require('classnames');
-var FormattedRelative = require('react-intl').FormattedRelative;
-var React = require('react');
+const classNames = require('classnames');
+const FormattedRelative = require('react-intl').FormattedRelative;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var FlexRow = require('../flex-row/flex-row.jsx');
+const FlexRow = require('../flex-row/flex-row.jsx');
 
 require('./social-message.scss');
 
-var SocialMessage = React.createClass({
-    type: 'SocialMessage',
-    propTypes: {
-        as: React.PropTypes.string,
-        datetime: React.PropTypes.string.isRequired,
-        iconSrc: React.PropTypes.string,
-        iconAlt: React.PropTypes.string,
-        imgClassName: React.PropTypes.string
-    },
-    getDefaultProps: function () {
-        return {
-            as: 'li'
-        };
-    },
-    render: function () {
-        var classes = classNames(
-            'social-message',
-            this.props.className
-        );
-        var imgClass = classNames(
-            'social-message-icon',
-            this.props.imgClassName
-        );
-        return (
-            <this.props.as className={classes}>
-                <FlexRow className="mod-social-message">
-                    <div className="social-message-content">
-                        {typeof this.props.iconSrc !== 'undefined' ? [
-                            <img
-                                key="social-message-icon"
-                                className={imgClass}
-                                src={this.props.iconSrc}
-                                alt={this.props.iconAlt}
-                            />
-                        ] : []}
-                        <div>
-                            {this.props.children}
-                        </div>
-                    </div>
-                    <span className="social-message-date">
-                        <FormattedRelative value={new Date(this.props.datetime)} />
-                    </span>
-                </FlexRow>
-            </this.props.as>
-        );
-    }
-});
+const SocialMessage = props => (
+    <props.as className={classNames('social-message', props.className)}>
+        <FlexRow className="mod-social-message">
+            <div className="social-message-content">
+                {typeof props.iconSrc === 'undefined' ? [] : [
+                    <img
+                        alt={props.iconAlt}
+                        className={classNames('social-message-icon', props.imgClassName)}
+                        key="social-message-icon"
+                        src={props.iconSrc}
+                    />
+                ]}
+                <div>
+                    {props.children}
+                </div>
+            </div>
+            <span className="social-message-date">
+                <FormattedRelative value={new Date(props.datetime)} />
+            </span>
+        </FlexRow>
+    </props.as>
+);
+
+SocialMessage.propTypes = {
+    as: PropTypes.string, // eslint-disable-line react/no-unused-prop-types
+    children: PropTypes.node,
+    className: PropTypes.string,
+    datetime: PropTypes.string.isRequired,
+    iconAlt: PropTypes.string,
+    iconSrc: PropTypes.string,
+    imgClassName: PropTypes.string
+};
+
+SocialMessage.defaultProps = {
+    as: 'li'
+};
 
 module.exports = SocialMessage;
diff --git a/src/components/spinner/spinner.jsx b/src/components/spinner/spinner.jsx
index 68fab1950..fa3af8050 100644
--- a/src/components/spinner/spinner.jsx
+++ b/src/components/spinner/spinner.jsx
@@ -1,20 +1,18 @@
-var range = require('lodash.range');
-var React = require('react');
+const range = require('lodash.range');
+const React = require('react');
 
 require('./spinner.scss');
 
-var Spinner = React.createClass({
-    // Adapted from http://tobiasahlin.com/spinkit/
-    type: 'Spinner',
-    render: function () {
-        return (
-            <div className="spinner">
-                {range(1,13).map(function (id) {
-                    return <div className={'circle' + id + ' circle'}></div>;
-                })}
-            </div>
-        );
-    }
-});
+// Adapted from http://tobiasahlin.com/spinkit/
+const Spinner = () => (
+    <div className="spinner">
+        {range(1, 13).map(id => (
+            <div
+                className={`circle${id} circle`}
+                key={`circle${id}`}
+            />
+        ))}
+    </div>
+);
 
 module.exports = Spinner;
diff --git a/src/components/stepnavigation/stepnavigation.jsx b/src/components/stepnavigation/stepnavigation.jsx
index 1f049e3ab..4988ffa1b 100644
--- a/src/components/stepnavigation/stepnavigation.jsx
+++ b/src/components/stepnavigation/stepnavigation.jsx
@@ -1,28 +1,29 @@
-var classNames = require('classnames');
-var React = require('react');
+const classNames = require('classnames');
+const PropTypes = require('prop-types');
+const React = require('react');
 
 require('./stepnavigation.scss');
 
-var StepNavigation = React.createClass({
-    type: 'Navigation',
-    render: function () {
-        return (
-            <ul className={classNames('step-navigation', this.props.className)}>
-                {Array.apply(null, Array(this.props.steps)).map(function (v, step) {
-                    return (
-                        <li key={step}
-                            className={classNames({
-                                active: step < this.props.active,
-                                selected: step === this.props.active
-                            })}
-                        >
-                            <div className="indicator" />
-                        </li>
-                    );
-                }.bind(this))}
-            </ul>
-        );
-    }
-});
+const StepNavigation = props => (
+    <ul className={classNames('step-navigation', props.className)}>
+        {Array.apply(null, Array(props.steps)).map((v, step) => (
+            <li
+                className={classNames({
+                    active: step < props.active,
+                    selected: step === props.active
+                })}
+                key={step}
+            >
+                <div className="indicator" />
+            </li>
+        ))}
+    </ul>
+);
+
+StepNavigation.propTypes = {
+    active: PropTypes.number,
+    className: PropTypes.string,
+    steps: PropTypes.number
+};
 
 module.exports = StepNavigation;
diff --git a/src/components/subnavigation/subnavigation.jsx b/src/components/subnavigation/subnavigation.jsx
index 62f4a78bb..e9d964975 100644
--- a/src/components/subnavigation/subnavigation.jsx
+++ b/src/components/subnavigation/subnavigation.jsx
@@ -1,36 +1,38 @@
-var classNames = require('classnames');
-var React = require('react');
+const classNames = require('classnames');
+const PropTypes = require('prop-types');
+const React = require('react');
 
 require('./subnavigation.scss');
 
-/**
+/*
  * Container for a custom, horizontal list of navigation elements
  * that can be displayed within a view or component.
  */
-var SubNavigation = React.createClass({
-    type: 'SubNavigation',
-    getDefaultProps: function () {
-        return {
-            align: 'middle'
-        };
-    },
-    render: function () {
-        var classes = classNames(
+const SubNavigation = props => (
+    <div
+        className={classNames(
             [
                 'sub-nav',
-                this.props.className
+                props.className
             ],
             {
-                'sub-nav-align-left': this.props.align === 'left',
-                'sub-nav-align-right': this.props.align === 'right'
+                'sub-nav-align-left': props.align === 'left',
+                'sub-nav-align-right': props.align === 'right'
             }
-        );
-        return (
-            <div className={classes}>
-                {this.props.children}
-            </div>
-        );
-    }
-});
+        )}
+    >
+        {props.children}
+    </div>
+);
+
+SubNavigation.propTypes = {
+    align: PropTypes.string,
+    children: PropTypes.node,
+    className: PropTypes.string
+};
+
+SubNavigation.defaultProps = {
+    align: 'middle'
+};
 
 module.exports = SubNavigation;
diff --git a/src/components/tabs/tabs.jsx b/src/components/tabs/tabs.jsx
index dd816c1ac..cc637ff56 100644
--- a/src/components/tabs/tabs.jsx
+++ b/src/components/tabs/tabs.jsx
@@ -1,28 +1,26 @@
-var classNames = require('classnames');
-var SubNavigation = require('../../components/subnavigation/subnavigation.jsx');
-var React = require('react');
+const classNames = require('classnames');
+const PropTypes = require('prop-types');
+const React = require('react');
+
+const SubNavigation = require('../../components/subnavigation/subnavigation.jsx');
 
 require('./tabs.scss');
 
-/**
+/*
  * Container for a custom, horizontal list of navigation elements
  * that can be displayed within a view or component.
  */
-var Tabs = React.createClass({
-    type: 'Tabs',
-    render: function () {
-        var classes = classNames(
-            'tabs',
-            this.props.className
-        );
-        return (
-            <div className='tab-background'>
-                <SubNavigation className={classes}>
-                    {this.props.children}
-                </SubNavigation>
-            </div>
-        );
-    }
-});
+const Tabs = props => (
+    <div className="tab-background">
+        <SubNavigation className={classNames('tabs', props.className)}>
+            {props.children}
+        </SubNavigation>
+    </div>
+);
+
+Tabs.propTypes = {
+    children: PropTypes.node,
+    className: PropTypes.string
+};
 
 module.exports = Tabs;
diff --git a/src/components/teacher-banner/teacher-banner.jsx b/src/components/teacher-banner/teacher-banner.jsx
index b24cebcc0..3ccf8ea89 100644
--- a/src/components/teacher-banner/teacher-banner.jsx
+++ b/src/components/teacher-banner/teacher-banner.jsx
@@ -1,86 +1,100 @@
-var classNames = require('classnames');
-var connect = require('react-redux').connect;
-var React = require('react');
+const classNames = require('classnames');
+const connect = require('react-redux').connect;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var sessionActions = require('../../redux/session.js');
+const sessionActions = require('../../redux/session.js');
 
-var TitleBanner = require('../title-banner/title-banner.jsx');
-var Button = require('../forms/button.jsx');
-var FlexRow = require('../flex-row/flex-row.jsx');
+const TitleBanner = require('../title-banner/title-banner.jsx');
+const Button = require('../forms/button.jsx');
+const FlexRow = require('../flex-row/flex-row.jsx');
 
 require('./teacher-banner.scss');
 
-var TeacherBanner = React.createClass({
-    type: 'TeacherBanner',
-    getDefaultProps: function () {
-        return {
-            messages: {
-                'teacherbanner.greeting': 'Hi',
-                'teacherbanner.subgreeting': 'Teacher Account',
-                'teacherbanner.classesButton': 'My Classes',
-                'teacherbanner.resourcesButton': 'Educator Resources',
-                'teacherbanner.faqButton': 'Teacher Account FAQ'
-            },
-            session: {}
-        };
-    },
-    render: function () {
-        var classes = classNames(
-            'teacher-banner',
-            this.props.className
-        );
-        return (
-            <TitleBanner className={classes}>
-                <FlexRow className="inner">
-                    <div className="welcome">
-                        {this.props.session.status === sessionActions.Status.FETCHED ? (
-                            this.props.session.session.user ? [
-                                <h3 key="greeting">
-                                    {this.props.messages['teacherbanner.greeting']},{' '}
-                                    {this.props.session.session.user.username}
-                                </h3>,
-                                <p
-                                    key="subgreeting"
-                                    className="title-banner-p"
-                                >
-                                    {this.props.messages['teacherbanner.subgreeting']}
-                                </p>
-                            ] : []
-                        ): []}
-                    </div>
-                    <FlexRow className="quick-links">
-                        {this.props.session.status === sessionActions.Status.FETCHED ? (
-                            this.props.session.session.user ? [
-                                <a href="/educators/classes" key="classes-button">
-                                    <Button>
-                                        {this.props.messages['teacherbanner.classesButton']}
-                                    </Button>
-                                </a>,
-                                <a href="/info/educators" key="resources-button">
-                                    <Button>
-                                        {this.props.messages['teacherbanner.resourcesButton']}
-                                    </Button>
-                                </a>,
-                                <a href="/educators/faq" key="faq-button">
-                                    <Button>
-                                        {this.props.messages['teacherbanner.faqButton']}
-                                    </Button>
-                                </a>
-                            ] : []
-                        ): []}
-                    </FlexRow>
-                </FlexRow>
-            </TitleBanner>
-        );
-    }
-});
+const TeacherBanner = props => (
+    <TitleBanner className={classNames('teacher-banner', props.className)}>
+        <FlexRow className="inner">
+            <div className="welcome">
+                {props.sessionStatus === sessionActions.Status.FETCHED ? (
+                    props.user ? [
+                        <h3 key="greeting">
+                            {props.messages['teacherbanner.greeting']},{' '}
+                            {props.user.username}
+                        </h3>,
+                        <p
+                            className="title-banner-p"
+                            key="subgreeting"
+                        >
+                            {props.messages['teacherbanner.subgreeting']}
+                        </p>
+                    ] : []
+                ) : []}
+            </div>
+            <FlexRow className="quick-links">
+                {props.sessionStatus === sessionActions.Status.FETCHED ? (
+                    props.user ? [
+                        <a
+                            href="/educators/classes"
+                            key="classes-button"
+                        >
+                            <Button>
+                                {props.messages['teacherbanner.classesButton']}
+                            </Button>
+                        </a>,
+                        <a
+                            href="/info/educators"
+                            key="resources-button"
+                        >
+                            <Button>
+                                {props.messages['teacherbanner.resourcesButton']}
+                            </Button>
+                        </a>,
+                        <a
+                            href="/educators/faq"
+                            key="faq-button"
+                        >
+                            <Button>
+                                {props.messages['teacherbanner.faqButton']}
+                            </Button>
+                        </a>
+                    ] : []
+                ) : []}
+            </FlexRow>
+        </FlexRow>
+    </TitleBanner>
+);
 
-var mapStateToProps = function (state) {
-    return {
-        session: state.session
-    };
+TeacherBanner.propTypes = {
+    className: PropTypes.string,
+    messages: PropTypes.shape({
+        'teacherbanner.greeting': PropTypes.string,
+        'teacherbanner.subgreeting': PropTypes.string,
+        'teacherbanner.classesButton': PropTypes.string,
+        'teacherbanner.resourcesButton': PropTypes.string,
+        'teacherbanner.faqButton': PropTypes.string
+    }),
+    sessionStatus: PropTypes.string,
+    user: PropTypes.shape({
+        username: PropTypes.string
+    })
 };
 
-var ConnectedTeacherBanner = connect(mapStateToProps)(TeacherBanner);
+TeacherBanner.defaultProps = {
+    messages: {
+        'teacherbanner.greeting': 'Hi',
+        'teacherbanner.subgreeting': 'Teacher Account',
+        'teacherbanner.classesButton': 'My Classes',
+        'teacherbanner.resourcesButton': 'Educator Resources',
+        'teacherbanner.faqButton': 'Teacher Account FAQ'
+    },
+    user: {}
+};
+
+const mapStateToProps = state => ({
+    sessionStatus: state.session.status,
+    user: state.session.session.user
+});
+
+const ConnectedTeacherBanner = connect(mapStateToProps)(TeacherBanner);
 
 module.exports = ConnectedTeacherBanner;
diff --git a/src/components/thumbnail/thumbnail.jsx b/src/components/thumbnail/thumbnail.jsx
index 314061ba0..300c65955 100644
--- a/src/components/thumbnail/thumbnail.jsx
+++ b/src/components/thumbnail/thumbnail.jsx
@@ -1,186 +1,168 @@
-var classNames = require('classnames');
-var React = require('react');
+const classNames = require('classnames');
+const PropTypes = require('prop-types');
+const React = require('react');
 
 require('./thumbnail.scss');
 
-var Thumbnail = React.createClass({
-    type: 'Thumbnail',
-    propTypes: {
-        src: React.PropTypes.string
-    },
-    getInitialState: function () {
-        return {
-            srcFallback: false,
-            avatarFallback: false
-        };
-    },
-    getDefaultProps: function () {
-        return {
-            href: '#',
-            title: 'Project',
-            src: '',
-            srcDefault: 'https://uploads.scratch.mit.edu/projects/thumbnails/default.png',
-            avatar: '',
-            avatarDefault: 'https://uploads.scratch.mit.edu/users/avatars/default.png',
-            type: 'project',
-            showLoves: false,
-            showFavorites: false,
-            showRemixes: false,
-            showViews: false,
-            showAvatar: false,
-            linkTitle: true,
-            alt: ''
-        };
-    },
-    handleSrcError: function () {
-        this.setState({srcFallback: true});
-    },
-    handleAvatarError: function () {
-        this.setState({avatarFallback: true});
-    },
-    render: function () {
-        var classes = classNames(
-            'thumbnail',
-            this.props.type,
-            this.props.className
-        );
-        var extra = [];
-        var info = [];
+const Thumbnail = props => {
+    const extra = [];
+    const info = [];
 
-        if (this.props.loves && this.props.showLoves) {
-            extra.push(
-                <div
-                    key="loves"
-                    className="thumbnail-loves"
-                    title={this.props.loves + ' loves'}>
-                    {this.props.loves}
-                </div>
-            );
-        }
-        if (this.props.favorites && this.props.showFavorites) {
-            extra.push(
-                <div
-                    key="favorites"
-                    className="thumbnail-favorites"
-                    title={this.favorites + ' favorites'}>
-                    {this.props.favorites}
-                </div>
-            );
-        }
-        if (this.props.remixes && this.props.showRemixes) {
-            extra.push(
-                <div
-                    key="remixes"
-                    className="thumbnail-remixes"
-                    title={this.props.remixes + ' remixes'}
-                >
-                    {this.props.remixes}
-                </div>
-            );
-        }
-        if (this.props.views && this.props.showViews) {
-            extra.push(
-                <div
-                    key="views"
-                    className="thumbnail-views"
-                    title={this.props.views + ' views'}
-                >
-                    {this.props.views}
-                </div>
-            );
-        }
-
-        var imgElement, titleElement, avatarElement;
-        if (this.props.linkTitle) {
-            if (this.state.srcFallback) {
-                imgElement = (
-                    <a
-                        className="thumbnail-image"
-                        href={this.props.href}
-                        key="imgElement"
-                    >
-                        <img
-                            alt={this.props.alt}
-                            src={this.props.srcDefault}
-                        />
-                    </a>
-                );
-            } else {
-                imgElement = (
-                    <a
-                        className="thumbnail-image"
-                        href={this.props.href}
-                        key="imgElement"
-                    >
-                        <img
-                            alt={this.props.alt}
-                            src={this.props.src}
-                            onError={this.handleSrcError}
-                        />
-                    </a>
-                );
-            }
-            titleElement = (
-                <a href={this.props.href} key="titleElement">
-                    {this.props.title}
-                </a>
-            );
-        } else {
-            imgElement = <img src={this.props.src} />;
-            titleElement = this.props.title;
-        }
-
-        info.push(titleElement);
-
-        if (this.props.creator) {
-            info.push(
-                <div key="creator" className="thumbnail-creator">
-                    <a href={'/users/' + this.props.creator + '/'}>{this.props.creator}</a>
-                </div>
-            );
-        }
-
-        if (this.props.avatar && this.props.showAvatar) {
-            if (this.state.avatarFallback) {
-                avatarElement = (
-                    <a
-                        className="creator-image"
-                        href={'/users/' + this.props.creator + '/'}
-                    >
-                        <img
-                            alt={this.props.creator}
-                            src={this.props.avatarDefault}
-                        />
-                    </a>
-                );
-            } else {
-                avatarElement = (
-                    <a
-                        className="creator-image"
-                        href={'/users/' + this.props.creator + '/'}
-                    >
-                        <img
-                            alt={this.props.creator}
-                            src={this.props.avatar}
-                            onError={this.handleAvatarError}
-                        />
-                    </a>
-                );
-            }
-        }
-        return (
-            <div className={classes} >
-                {imgElement}
-                <div className="thumbnail-info">
-                    {avatarElement}
-                    <div className="thumbnail-title">
-                        {info}
-                    </div>
-                </div>
-                {extra}
+    if (props.loves && props.showLoves) {
+        extra.push(
+            <div
+                className="thumbnail-loves"
+                key="loves"
+                title={`${props.loves} loves`}
+            >
+                {props.loves}
             </div>
         );
     }
-});
+    if (props.favorites && props.showFavorites) {
+        extra.push(
+            <div
+                className="thumbnail-favorites"
+                key="favorites"
+                title={`${props.favorites} favorites`}
+            >
+                {props.favorites}
+            </div>
+        );
+    }
+    if (props.remixes && props.showRemixes) {
+        extra.push(
+            <div
+                className="thumbnail-remixes"
+                key="remixes"
+                title={`${props.remixes} remixes`}
+            >
+                {props.remixes}
+            </div>
+        );
+    }
+    if (props.views && props.showViews) {
+        extra.push(
+            <div
+                className="thumbnail-views"
+                key="views"
+                title={`${props.views} views`}
+            >
+                {props.views}
+            </div>
+        );
+    }
+
+    let imgElement;
+    let titleElement;
+    let avatarElement;
+
+    if (props.linkTitle) {
+        imgElement = (
+            <a
+                className="thumbnail-image"
+                href={props.href}
+                key="imgElement"
+            >
+                <img
+                    alt={props.alt}
+                    src={props.src}
+                />
+            </a>
+        );
+        titleElement = (
+            <a
+                href={props.href}
+                key="titleElement"
+            >
+                {props.title}
+            </a>
+        );
+    } else {
+        imgElement = <img src={props.src} />;
+        titleElement = props.title;
+    }
+
+    info.push(titleElement);
+
+    if (props.creator) {
+        info.push(
+            <div
+                className="thumbnail-creator"
+                key="creator"
+            >
+                <a href={`/users/${props.creator}/`}>{props.creator}</a>
+            </div>
+        );
+    }
+
+    if (props.avatar && props.showAvatar) {
+        avatarElement = (
+            <a
+                className="creator-image"
+                href={`/users/${props.creator}/`}
+            >
+                <img
+                    alt={props.creator}
+                    src={props.avatar}
+                />
+            </a>
+        );
+    }
+    return (
+        <div
+            className={classNames(
+                'thumbnail',
+                props.type,
+                props.className
+            )}
+        >
+            {imgElement}
+            <div className="thumbnail-info">
+                {avatarElement}
+                <div className="thumbnail-title">
+                    {info}
+                </div>
+            </div>
+            {extra}
+        </div>
+    );
+};
+
+Thumbnail.propTypes = {
+    alt: PropTypes.string,
+    avatar: PropTypes.string,
+    className: PropTypes.string,
+    creator: PropTypes.string,
+    favorites: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
+    href: PropTypes.string,
+    linkTitle: PropTypes.bool,
+    loves: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
+    remixes: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
+    showAvatar: PropTypes.bool,
+    showFavorites: PropTypes.bool,
+    showLoves: PropTypes.bool,
+    showRemixes: PropTypes.bool,
+    showViews: PropTypes.bool,
+    src: PropTypes.string,
+    title: PropTypes.string,
+    type: PropTypes.string,
+    views: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
+};
+
+Thumbnail.defaultProps = {
+    alt: '',
+    avatar: '',
+    href: '#',
+    linkTitle: true,
+    showAvatar: false,
+    showFavorites: false,
+    showLoves: false,
+    showRemixes: false,
+    showViews: false,
+    src: '',
+    title: 'Project',
+    type: 'project'
+};
 
 module.exports = Thumbnail;
diff --git a/src/components/title-banner/title-banner.jsx b/src/components/title-banner/title-banner.jsx
index 7b6dd722f..60cf13c6b 100644
--- a/src/components/title-banner/title-banner.jsx
+++ b/src/components/title-banner/title-banner.jsx
@@ -1,21 +1,18 @@
-var classNames = require('classnames');
-var React = require('react');
+const classNames = require('classnames');
+const PropTypes = require('prop-types');
+const React = require('react');
 
 require('./title-banner.scss');
 
-var TitleBanner = React.createClass({
-    type: 'TitleBanner',
-    render: function () {
-        var classes = classNames(
-            'title-banner',
-            this.props.className
-        );
-        return (
-            <div className={classes}>
-                {this.props.children}
-            </div>
-        );
-    }
-});
+const TitleBanner = props => (
+    <div className={classNames('title-banner', props.className)}>
+        {props.children}
+    </div>
+);
+
+TitleBanner.propTypes = {
+    children: PropTypes.node,
+    className: PropTypes.string
+};
 
 module.exports = TitleBanner;
diff --git a/src/components/tooltip/tooltip.jsx b/src/components/tooltip/tooltip.jsx
index b5521878d..9bd441b9e 100644
--- a/src/components/tooltip/tooltip.jsx
+++ b/src/components/tooltip/tooltip.jsx
@@ -1,33 +1,39 @@
-var classNames = require('classnames');
-var React = require('react');
+const classNames = require('classnames');
+const PropTypes = require('prop-types');
+const React = require('react');
 
 require('./tooltip.scss');
 
-var Tooltip = React.createClass({
-    type: 'Tooltip',
-    getDefaultProps: function () {
-        return {
-            title: '',
-            tipContent: ''
-        };
-    },
-    render: function () {
-        var classes = classNames(
+const Tooltip = props => (
+    <span
+        className={classNames(
             'tooltip',
-            this.props.className,
-            {overmax: (this.props.currentCharacters > this.props.maxCharacters)}
-        );
-        return (
-            <span className={classes}>
-                <span className="tip">
-                    <img src="/svgs/tooltip/info.svg" alt="info icon" />
-                </span>
-                <span className="expand">
-                    {this.props.tipContent}
-                </span>
-            </span>
-        );
-    }
-});
+            props.className,
+            {overmax: (props.currentCharacters > props.maxCharacters)}
+        )}
+    >
+        <span className="tip">
+            <img
+                alt="info icon"
+                src="/svgs/tooltip/info.svg"
+            />
+        </span>
+        <span className="expand">
+            {props.tipContent}
+        </span>
+    </span>
+);
+
+Tooltip.propTypes = {
+    className: PropTypes.string,
+    currentCharacters: PropTypes.number,
+    maxCharacters: PropTypes.number,
+    tipContent: PropTypes.node
+};
+
+Tooltip.defaultProps = {
+    title: '',
+    tipContent: ''
+};
 
 module.exports = Tooltip;
diff --git a/src/components/ttt-tile/ttt-tile.jsx b/src/components/ttt-tile/ttt-tile.jsx
index 4bcb3d109..cd71ec37a 100644
--- a/src/components/ttt-tile/ttt-tile.jsx
+++ b/src/components/ttt-tile/ttt-tile.jsx
@@ -1,58 +1,68 @@
-var classNames = require('classnames');
-var React = require('react');
-var FormattedMessage = require('react-intl').FormattedMessage;
+const classNames = require('classnames');
+const FormattedMessage = require('react-intl').FormattedMessage;
+const PropTypes = require('prop-types');
+const React = require('react');
 
 require('../forms/button.scss');
 require('./ttt-tile.scss');
 
-var TTTTile = React.createClass({
-    type: 'TTTTile',
-    propTypes: {
-        title: React.PropTypes.string.isRequired,
-        description: React.PropTypes.string,
-        thumbUrl: React.PropTypes.string.isRequired,
-        tutorialLoc: React.PropTypes.string.isRequired,
-        onGuideClick: React.PropTypes.func
-    },
-    render: function () {
-        var classes = classNames(
-            'ttt-tile',
-            this.props.className
-        );
-        return (
-            <div className={classes} >
-                <a href={this.props.tutorialLoc}>
-                    <div className="ttt-tile-tutorial">
-                        <div className="ttt-tile-image">
-                            <img className="ttt-tile-image-img" src={this.props.thumbUrl} alt="" />
-                            <div className="ttt-tile-image-try">
-                                <div className="button mod-ttt-try-button">
-                                    <FormattedMessage id="tile.tryIt" />
-                                </div>
-                            </div>
-                        </div>
-                        <div className="ttt-tile-info">
-
-                            <div className="ttt-tile-tag">
-                                <FormattedMessage id='ttt.tutorial' defaultMessage='Tutorial'/>
-                            </div>
-                            <h4 className="ttt-tile-title">{this.props.title}</h4>
-                            <p className="ttt-tile-description">
-                                {this.props.description}
-                            </p>
+const TTTTile = props => (
+    <div className={classNames('ttt-tile', props.className)}>
+        <a href={props.tutorialLoc}>
+            <div className="ttt-tile-tutorial">
+                <div className="ttt-tile-image">
+                    <img
+                        alt=""
+                        className="ttt-tile-image-img"
+                        src={props.thumbUrl}
+                    />
+                    <div className="ttt-tile-image-try">
+                        <div className="button mod-ttt-try-button">
+                            <FormattedMessage id="tile.tryIt" />
                         </div>
                     </div>
+                </div>
+                <div className="ttt-tile-info">
 
-                </a>
-                {this.props.onGuideClick && (
-                    <div className="ttt-tile-guides" onClick={this.props.onGuideClick}>
-                        <FormattedMessage id='tile.guides' defaultMessage='See Cards and Guides'/>
-                        <img className="ttt-tile-open-modal" src="/svgs/modal/open-blue.svg" />
+                    <div className="ttt-tile-tag">
+                        <FormattedMessage
+                            defaultMessage="Tutorial"
+                            id="ttt.tutorial"
+                        />
                     </div>
-                )}
+                    <h4 className="ttt-tile-title">{props.title}</h4>
+                    <p className="ttt-tile-description">
+                        {props.description}
+                    </p>
+                </div>
             </div>
-        );
-    }
-});
+
+        </a>
+        {props.onGuideClick && (
+            <div
+                className="ttt-tile-guides"
+                onClick={props.onGuideClick}
+            >
+                <FormattedMessage
+                    defaultMessage="See Cards and Guides"
+                    id="tile.guides"
+                />
+                <img
+                    className="ttt-tile-open-modal"
+                    src="/svgs/modal/open-blue.svg"
+                />
+            </div>
+        )}
+    </div>
+);
+
+TTTTile.propTypes = {
+    className: PropTypes.string,
+    description: PropTypes.string,
+    onGuideClick: PropTypes.func,
+    thumbUrl: PropTypes.string.isRequired,
+    title: PropTypes.string.isRequired,
+    tutorialLoc: PropTypes.string.isRequired
+};
 
 module.exports = TTTTile;
diff --git a/src/components/welcome/welcome.jsx b/src/components/welcome/welcome.jsx
index 4cca43997..76f1dcf53 100644
--- a/src/components/welcome/welcome.jsx
+++ b/src/components/welcome/welcome.jsx
@@ -1,69 +1,81 @@
-var React = require('react');
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var Box = require('../box/box.jsx');
+const Box = require('../box/box.jsx');
 
 require('./welcome.scss');
 
-var Welcome = React.createClass({
-    type: 'Welcome',
-    propTypes: {
-        onDismiss: React.PropTypes.func
-    },
-    getDefaultProps: function () {
-        return {
-            messages: {
-                'welcome.welcomeToScratch': 'Welcome to Scratch!',
-                'welcome.learn': 'Learn how to make a project in Scratch',
-                'welcome.tryOut': 'Try out starter projects',
-                'welcome.connect': 'Connect with other Scratchers'
-            }
-        };
-    },
-    render: function () {
-        return (
-            <Box title={this.props.messages['welcome.welcomeToScratch']}
-                 className="welcome"
-                 moreTitle="x"
-                 moreHref="#"
-                 moreProps={{
-                     className: 'close',
-                     title: 'Dismiss',
-                     onClick: this.props.onDismiss
-                 }}>
+const Welcome = props => (
+    <Box
+        className="welcome"
+        moreHref="#"
+        moreProps={{
+            className: 'close',
+            title: 'Dismiss',
+            onClick: props.onDismiss
+        }}
+        moreTitle="x"
+        title={props.messages['welcome.welcomeToScratch']}
+    >
+        <div className="welcome-col blue">
+            <h4>
+                <a href="/projects/editor/?tip_bar=getStarted">
+                    {props.messages['welcome.learn']}
+                </a>
+            </h4>
+            <a href="/projects/editor/?tip_bar=getStarted">
+                <img
+                    alt="Get Started"
+                    src="/images/welcome-learn.png"
+                />
+            </a>
+        </div>
+        <div className="welcome-col green">
+            <h4>
+                <a href="/starter_projects/">
+                    {props.messages['welcome.tryOut']}
+                </a>
+            </h4>
+            <a href="/starter_projects/">
+                <img
+                    alt="Starter Projects"
+                    src="/images/welcome-try.png"
+                />
+            </a>
+        </div>
+        <div className="welcome-col pink">
+            <h4>
+                <a href="/studios/146521/">
+                    {props.messages['welcome.connect']}
+                </a>
+            </h4>
+            <a href="/studios/146521/">
+                <img
+                    alt="Connect"
+                    src="/images/welcome-connect.png"
+                />
+            </a>
+        </div>
+    </Box>
+);
 
-                <div className="welcome-col blue">
-                    <h4>
-                        <a href="/projects/editor/?tip_bar=getStarted">
-                            {this.props.messages['welcome.learn']}
-                        </a>
-                    </h4>
-                    <a href="/projects/editor/?tip_bar=getStarted">
-                        <img src="/images/welcome-learn.png" alt="Get Started" />
-                    </a>
-                </div>
-                <div className="welcome-col green">
-                    <h4>
-                        <a href="/starter_projects/">
-                            {this.props.messages['welcome.tryOut']}
-                        </a>
-                    </h4>
-                    <a href="/starter_projects/">
-                        <img src="/images/welcome-try.png" alt="Starter Projects" />
-                    </a>
-                </div>
-                <div className="welcome-col pink">
-                    <h4>
-                        <a href="/studios/146521/">
-                            {this.props.messages['welcome.connect']}
-                        </a>
-                    </h4>
-                    <a href="/studios/146521/">
-                        <img src="/images/welcome-connect.png" alt="Connect" />
-                    </a>
-                </div>
-            </Box>
-        );
+Welcome.propTypes = {
+    messages: PropTypes.shape({
+        'welcome.welcomeToScratch': PropTypes.string,
+        'welcome.learn': PropTypes.string,
+        'welcome.tryOut': PropTypes.string,
+        'welcome.connect': PropTypes.string
+    }),
+    onDismiss: PropTypes.func
+};
+
+Welcome.defaultProps = {
+    messages: {
+        'welcome.welcomeToScratch': 'Welcome to Scratch!',
+        'welcome.learn': 'Learn how to make a project in Scratch',
+        'welcome.tryOut': 'Try out starter projects',
+        'welcome.connect': 'Connect with other Scratchers'
     }
-});
+};
 
 module.exports = Welcome;
diff --git a/src/init.js b/src/init.js
index d97d79646..85e4b643f 100644
--- a/src/init.js
+++ b/src/init.js
@@ -1,12 +1,12 @@
-var jar = require('./lib/jar');
-var Raven = require('raven-js');
+const jar = require('./lib/jar');
+const Raven = require('raven-js');
 
 /**
  * -----------------------------------------------------------------------------
  * Error handling
  * -----------------------------------------------------------------------------
  */
-(function () {
+(() => {
     if (process.env.SENTRY_DSN !== '') {
         Raven.config(process.env.SENTRY_DSN).install();
     }
@@ -17,22 +17,22 @@ var Raven = require('raven-js');
  * L10N
  * -----------------------------------------------------------------------------
  */
-(function () {
-    /**
+(() => {
+    /*
      * Bind locale code from cookie if available. Uses navigator language API as a fallback.
      *
      * @return {string}
      */
-    function updateLocale () {
-        var obj = jar.get('scratchlanguage');
+    const updateLocale = () => {
+        let obj = jar.get('scratchlanguage');
         if (typeof obj === 'undefined') {
             obj = window.navigator.userLanguage || window.navigator.language;
-            if (['pt','pt-pt','PT','PT-PT'].indexOf(obj) !== -1) {
+            if (['pt', 'pt-pt', 'PT', 'PT-PT'].indexOf(obj) !== -1) {
                 obj = 'pt-br'; // default Portuguese users to Brazilian Portuguese due to our user base. Added in 2.2.5.
             }
         }
         return obj;
-    }
+    };
 
     window._locale = updateLocale();
 })();
diff --git a/src/l10n.json b/src/l10n.json
index 843aa5d32..e6f266126 100644
--- a/src/l10n.json
+++ b/src/l10n.json
@@ -47,6 +47,9 @@
     "general.myClass": "My Class",
     "general.myClasses": "My Classes",
     "general.myStuff": "My Stuff",
+    "general.noDeletionTitle": "Your Account Will Not Be Deleted",
+    "general.noDeletionDescription": "Your account was scheduled for deletion but you logged in. Your account has been reactivated. If you didn’t request for your account to be deleted, you should {resetLink} to make sure your account is secure.",
+    "general.noDeletionLink": "change your password",
     "general.notRequired": "Not Required",
     "general.other": "Other",
     "general.offlineEditor": "Offline Editor",
diff --git a/src/lib/api.js b/src/lib/api.js
index 8ee18706f..c226d7929 100644
--- a/src/lib/api.js
+++ b/src/lib/api.js
@@ -1,9 +1,9 @@
-var defaults = require('lodash.defaults');
-var xhr = require('xhr');
+const defaults = require('lodash.defaults');
+const xhr = require('xhr');
 
-var jar  = require('./jar');
-var log = require('./log');
-var urlParams = require('./url-params');
+const jar = require('./jar');
+const log = require('./log');
+const urlParams = require('./url-params');
 
 /**
  * Helper method that constructs requests to the scratch api.
@@ -12,9 +12,11 @@ var urlParams = require('./url-params');
  *       CSRF forgeries (see: https://www.squarefree.com/securitytips/web-developers.html#CSRF)
  *
  * It also takes in other arguments specified in the xhr library spec.
+ * 
+ * @param  {object}   opts     optional xhr args (see above)
+ * @param  {Function} callback [description]
  */
-
-module.exports = function (opts, callback) {
+module.exports = (opts, callback) => {
     defaults(opts, {
         host: process.env.API_HOST,
         headers: {},
@@ -40,26 +42,29 @@ module.exports = function (opts, callback) {
         opts.headers['Content-Type'] = 'application/x-www-form-urlencoded';
     }
 
-    var apiRequest = function (opts) {
-        if (opts.host !== '') {
+    const apiRequest = options => {
+        if (options.host !== '') {
             if ('withCredentials' in new XMLHttpRequest()) {
-                opts.useXDR = false;
+                options.useXDR = false;
             } else {
                 // For IE < 10, we must use XDR for cross-domain requests. XDR does not support
                 // custom headers.
-                opts.useXDR = true;
-                delete opts.headers;
-                if (opts.authentication) {
-                    var authenticationParams = ['x-token=' + opts.authentication];
-                    var parts = opts.uri.split('?');
-                    var qs = (parts[1] || '').split('&').concat(authenticationParams).join('&');
-                    opts.uri = parts[0] + '?' + qs;
+                options.useXDR = true;
+                delete options.headers;
+                if (options.authentication) {
+                    const authenticationParams = [`x-token=${options.authentication}`];
+                    const parts = options.uri.split('?');
+                    const qs = (parts[1] || '')
+                        .split('&')
+                        .concat(authenticationParams)
+                        .join('&');
+                    options.uri = `${parts[0]}?${qs}`;
                 }
             }
         }
-        xhr(opts, function (err, res, body) {
+        xhr(options, (err, res, body) => {
             if (err) log.error(err);
-            if (opts.responseType === 'json' && typeof body === 'string') {
+            if (options.responseType === 'json' && typeof body === 'string') {
                 // IE doesn't parse responses as JSON without the json attribute,
                 // even with responseType: 'json'.
                 // See https://github.com/Raynos/xhr/issues/123
@@ -73,25 +78,25 @@ module.exports = function (opts, callback) {
             // [{success: true, redirect: "/location/to/redirect"}]
             try {
                 if ('redirect' in body[0]) window.location = body[0].redirect;
-            } catch (err) {
+            } catch (e) {
                 // do nothing
             }
             callback(err, body, res);
         });
-    }.bind(this);
+    };
 
     if (typeof jar.get('scratchlanguage') !== 'undefined') {
-        opts.headers['Accept-Language'] = jar.get('scratchlanguage') + ', en;q=0.8';
+        opts.headers['Accept-Language'] = `${jar.get('scratchlanguage')}, en;q=0.8`;
     }
     if (opts.authentication) {
         opts.headers['X-Token'] = opts.authentication;
     }
     if (opts.useCsrf) {
-        jar.use('scratchcsrftoken', '/csrf_token/', function (err, csrftoken) {
+        jar.use('scratchcsrftoken', '/csrf_token/', (err, csrftoken) => {
             if (err) return log.error('Error while retrieving CSRF token', err);
             opts.headers['X-CSRFToken'] = csrftoken;
             apiRequest(opts);
-        }.bind(this));
+        });
     } else {
         apiRequest(opts);
     }
diff --git a/src/lib/country-data.js b/src/lib/country-data.js
index 46e8ebbe5..93268c071 100644
--- a/src/lib/country-data.js
+++ b/src/lib/country-data.js
@@ -1,22 +1,28 @@
 module.exports = {};
-var countries = module.exports.data = require('iso-3166-2').data;
+const countries = module.exports.data = require('iso-3166-2').data;
 
-module.exports.countryOptions = Object.keys(countries).map(function (code) {
-    return {value: code.toLowerCase(), label: countries[code].name};
-}).sort(function (a, b) {
-    return a.label < b.label ? -1 : 1;
-});
-
-module.exports.subdivisionOptions =
-Object.keys(countries).reduce(function (subByCountry, code) {
-    subByCountry[code.toLowerCase()] = Object.keys(countries[code].sub).map(function (subCode) {
-        return {
-            value: subCode.toLowerCase(),
-            label: countries[code].sub[subCode].name,
-            type: countries[code].sub[subCode].type
-        };
-    }).sort(function (a, b) {
-        return a.label < b.label ? -1 : 1;
+module.exports.countryOptions = Object.keys(countries).map(code => ({
+    value: code.toLowerCase(),
+    label: countries[code].name
+}))
+    .sort((a, b) => {
+        if (a.label < b.label) {
+            return -1;
+        }
+        return 1;
     });
+
+module.exports.subdivisionOptions = Object.keys(countries).reduce((subByCountry, code) => {
+    subByCountry[code.toLowerCase()] = Object.keys(countries[code].sub).map(subCode => ({
+        value: subCode.toLowerCase(),
+        label: countries[code].sub[subCode].name,
+        type: countries[code].sub[subCode].type
+    }))
+        .sort((a, b) => {
+            if (a.label < b.label) {
+                return -1;
+            }
+            return 1;
+        });
     return subByCountry;
 }, {});
diff --git a/src/lib/format.js b/src/lib/format.js
deleted file mode 100644
index f54f084c5..000000000
--- a/src/lib/format.js
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * Takes an ISO time and returns a string representing how long ago the date
- * represents. For example, "2015-01-01T00:00:00" becomes "1 minute ago".
- *
- * Based on "JavaScript Pretty Date"
- * Copyright (c) 2011 John Resig (ejohn.org)
- * Licensed under the MIT and GPL licenses.
- */
-
-var Format = {
-    date: function (stamp) {
-        stamp = (stamp || '').replace(/-/g,'/').replace(/[TZ]/g,' ');
-
-        var date = new Date(stamp);
-        var diff = (((new Date()).getTime() - date.getTime()) / 1000);
-        var day_diff = Math.floor(diff / 86400);
-            
-        if (isNaN(day_diff) || day_diff < 0 || day_diff >= 31) {
-            return 'A while ago';
-        }
-                
-        return day_diff == 0 && (
-                diff < 60 && 'Just now' ||
-                diff < 120 && '1 minute ago' ||
-                diff < 3600 && Math.floor( diff / 60 ) + ' minutes ago' ||
-                diff < 7200 && '1 hour ago' ||
-                diff < 86400 && Math.floor( diff / 3600 ) + ' hours ago') ||
-            day_diff == 1 && 'Yesterday' ||
-            day_diff < 7 && day_diff + ' days ago' ||
-            day_diff < 31 && Math.ceil( day_diff / 7 ) + ' weeks ago';
-    }
-};
-
-module.exports = Format;
diff --git a/src/lib/frameless.js b/src/lib/frameless.js
index 9c63eb507..6c93deed5 100644
--- a/src/lib/frameless.js
+++ b/src/lib/frameless.js
@@ -1,9 +1,10 @@
 /* This file contains breakpoints from _frameless.scss, to be used in MediaQuery elements.
  * All units are in px, as per _frameless.scss and the MediaQuery default arguments.
  */
-
-module.exports = {
+const frameless = {
     desktop: 942,
     tablet: 640,
     mobile: 480
 };
+
+module.exports = frameless;
diff --git a/src/lib/intl.jsx b/src/lib/intl.jsx
index 12a76eb46..c29c53fab 100644
--- a/src/lib/intl.jsx
+++ b/src/lib/intl.jsx
@@ -1,20 +1,20 @@
-var requireAll = require('./require-all');
-var ReactIntl = require('react-intl');
+const requireAll = require('./require-all');
+const ReactIntl = require('react-intl');
 
-var allLocaleData = requireAll(require.context('react-intl/locale-data', true, /^\.\/.*\.js$/));
-var customLocaleData = require('../../custom-locales.json');
+const allLocaleData = requireAll(require.context('react-intl/locale-data', true, /^\.\/.*\.js$/));
+const customLocaleData = require('../../custom-locales.json');
 
 /**
   * Add all locales
   */
-for (var locale in allLocaleData) {
+for (const locale in allLocaleData) {
     ReactIntl.addLocaleData(allLocaleData[locale]);
 }
 
 /**
  * Add custom locales to react-intl if it doesn't have them.
  */
-for (var customLocale in customLocaleData) {
+for (const customLocale in customLocaleData) {
     ReactIntl.addLocaleData(customLocaleData[customLocale]);
 }
 
diff --git a/src/lib/jar.js b/src/lib/jar.js
index 343a3befa..afcc1b277 100644
--- a/src/lib/jar.js
+++ b/src/lib/jar.js
@@ -1,7 +1,7 @@
-var cookie = require('cookie');
-var defaults = require('lodash.defaults');
-var xhr = require('xhr');
-var pako = require('pako');
+const cookie = require('cookie');
+const defaults = require('lodash.defaults');
+const xhr = require('xhr');
+const pako = require('pako');
 
 /**
  * Module that handles coookie interactions.
@@ -11,16 +11,16 @@ var pako = require('pako');
  * set(name, value) – synchronously sets the cookie
  * use(name, uri, callback) – can by sync or async, gets cookie from the uri if not there.
  */
-var Jar = {
-    unsign: function (value, callback) {
+const Jar = {
+    unsign: (value, callback) => {
         // Return the usable content portion of a signed, compressed cookie generated by
         // Django's signing module
         // https://github.com/django/django/blob/stable/1.8.x/django/core/signing.py
         if (typeof value === 'undefined') return callback(null, value);
         
         try {
-            var b64Data = value.split(':')[0];
-            var decompress = false;
+            let b64Data = value.split(':')[0];
+            let decompress = false;
             if (b64Data[0] === '.') {
                 decompress = true;
                 b64Data = b64Data.substring(1);
@@ -29,24 +29,29 @@ var Jar = {
             // Django makes its base64 strings url safe by replacing + and / with - and _ respectively
             // using base64.urlsafe_b64encode
             // https://docs.python.org/2/library/base64.html#base64.b64encode
-            b64Data = b64Data.replace(/[-_]/g, function (c) {return {'-':'+', '_':'/'}[c]; });
-            var strData = atob(b64Data);
+            b64Data = b64Data.replace(
+                /[-_]/g,
+                c => ({
+                    '-': '+',
+                    '_': '/'
+                }[c])
+            );
+            let strData = atob(b64Data);
 
             if (decompress) {
-                var charData = strData.split('').map(function (c) { return c.charCodeAt(0); });
-                var binData = new Uint8Array(charData);
-                var data = pako.inflate(binData);
+                const charData = strData.split('').map(c => (c.charCodeAt(0)));
+                const binData = new Uint8Array(charData);
+                const data = pako.inflate(binData);
                 strData = String.fromCharCode.apply(null, new Uint16Array(data));
             }
-
             return callback(null, strData);
         } catch (e) {
             return callback(e);
         }
     },
-    get: function (name, callback) {
+    get: (name, callback) => {
         // Get cookie by name
-        var obj = cookie.parse(document.cookie) || {};
+        const obj = cookie.parse(document.cookie) || {};
 
         // Handle optional callback
         if (typeof callback === 'function') {
@@ -56,44 +61,44 @@ var Jar = {
 
         return obj[name];
     },
-    use: function (name, uri, callback) {
+    use: (name, uri, callback) => {
         // Attempt to get cookie
-        Jar.get(name, function (err, obj) {
+        Jar.get(name, (err, obj) => {
             if (typeof obj !== 'undefined') return callback(null, obj);
 
             // Make XHR request to cookie setter uri
             xhr({
                 uri: uri
-            }, function (err) {
-                if (err) return callback(err);
+            }, e => {
+                if (e) return callback(e);
                 Jar.get(name, callback);
             });
         });
     },
-    set: function (name, value, opts) {
+    set: (name, value, opts) => {
         opts = opts || {};
         defaults(opts, {
             expires: new Date(new Date().setYear(new Date().getFullYear() + 1))
         });
         opts.path = '/';
-        var obj = cookie.serialize(name, value, opts);
+        const obj = cookie.serialize(name, value, opts);
         document.cookie = obj;
     },
-    getUnsignedValue: function (cookieName, signedValue, callback) {
+    getUnsignedValue: (cookieName, signedValue, callback) => {
         // Get a value from a signed object
-        Jar.get(cookieName, function (err, value) {
+        Jar.get(cookieName, (err, value) => {
             if (err) return callback(err);
             if (typeof value === 'undefined') return callback(null, value);
             
-            Jar.unsign(value, function (err, contents) {
-                if (err) return callback(err);
+            Jar.unsign(value, (e, contents) => {
+                if (e) return callback(e);
                 
                 try {
-                    var data = JSON.parse(contents);
-                } catch (err) {
-                    return callback(err);
+                    const data = JSON.parse(contents);
+                    return callback(null, data[signedValue]);
+                } catch (error) {
+                    return callback(error);
                 }
-                return callback(null, data[signedValue]);
             });
         });
     }
diff --git a/src/lib/log.js b/src/lib/log.js
index 9a364044f..bbc8dac4a 100644
--- a/src/lib/log.js
+++ b/src/lib/log.js
@@ -1,4 +1,4 @@
-var minilog = require('minilog');
+const minilog = require('minilog');
 minilog.enable();
 
 module.exports = minilog('www');
diff --git a/src/lib/render.jsx b/src/lib/render.jsx
index eddfd707c..394eee801 100644
--- a/src/lib/render.jsx
+++ b/src/lib/render.jsx
@@ -1,21 +1,27 @@
-var redux = require('redux');
-var thunk = require('redux-thunk').default;
+const redux = require('redux');
+const thunk = require('redux-thunk').default;
 // JSX syntax transforms to React.createElement
-var React = require('react'); // eslint-disable-line
-var ReactDOM = require('react-dom');
-var StoreProvider = require('react-redux').Provider;
+const React = require('react'); // eslint-disable-line
+const ReactDOM = require('react-dom');
+const StoreProvider = require('react-redux').Provider;
 
-var IntlProvider = require('./intl.jsx').IntlProvider;
-var permissionsActions = require('../redux/permissions.js');
-var sessionActions = require('../redux/session.js');
-var reducer = require('../redux/reducer.js');
+const IntlProvider = require('./intl.jsx').IntlProvider;
+const permissionsActions = require('../redux/permissions.js');
+const sessionActions = require('../redux/session.js');
+const reducer = require('../redux/reducer.js');
 
 require('../main.scss');
 
-var render = function (jsx, element, reducers) {
+/**
+ * Function to render views into a full page
+ * @param  {object} jsx      jsx component of the view
+ * @param  {object} element  html element to render to on the template
+ * @param  {array}  reducers list of view-specific reducers
+ */
+const render = (jsx, element, reducers) => {
     // Get locale and messages from global namespace (see "init.js")
-    var locale = window._locale || 'en';
-    var messages = {};
+    let locale = window._locale || 'en';
+    let messages = {};
     if (typeof window._messages !== 'undefined') {
         if (typeof window._messages[locale] === 'undefined') {
             // Fall back on the split
@@ -28,8 +34,8 @@ var render = function (jsx, element, reducers) {
         messages = window._messages[locale];
     }
 
-    var allReducers = reducer(reducers);
-    var store = redux.createStore(
+    const allReducers = reducer(reducers);
+    const store = redux.createStore(
         allReducers,
         redux.applyMiddleware(thunk)
     );
@@ -37,7 +43,10 @@ var render = function (jsx, element, reducers) {
     // Render view component
     ReactDOM.render(
         <StoreProvider store={store}>
-            <IntlProvider locale={locale} messages={messages}>
+            <IntlProvider
+                locale={locale}
+                messages={messages}
+            >
                 {jsx}
             </IntlProvider>
         </StoreProvider>,
diff --git a/src/lib/require-all.js b/src/lib/require-all.js
index 028a18028..415eeecb9 100644
--- a/src/lib/require-all.js
+++ b/src/lib/require-all.js
@@ -1,5 +1,5 @@
-var requireAll = function (requireContext) {
-    return requireContext.keys().map(requireContext);
-};
+const requireAll = requireContext => (
+    requireContext.keys().map(requireContext)
+);
 
 module.exports = requireAll;
diff --git a/src/lib/shuffle.js b/src/lib/shuffle.js
index c32601375..11b6c4137 100644
--- a/src/lib/shuffle.js
+++ b/src/lib/shuffle.js
@@ -1,21 +1,19 @@
 /*
 * Function that shuffles an array using a Fisher-Yates shuffle.
 */
-
-module.exports.shuffle = function (arr) {
-    var i, j = 0;
-    var temp = null;
+module.exports.shuffle = arr => {
+    let i = 0;
+    let j = 0;
+    let temp = null;
     if (arr) {
-        var tempArray = arr.slice(0);
-    } else {
-        return arr;
+        const tempArray = arr.slice(0);
+        for (i = arr.length - 1; i > 0; i -= 1) {
+            j = Math.floor(Math.random() * (i + 1));
+            temp = tempArray[i];
+            tempArray[i] = tempArray[j];
+            tempArray[j] = temp;
+        }
+        return tempArray;
     }
-
-    for (i = arr.length - 1; i > 0; i -= 1) {
-        j = Math.floor(Math.random() * (i + 1));
-        temp = tempArray[i];
-        tempArray[i] = tempArray[j];
-        tempArray[j] = temp;
-    }
-    return tempArray;
+    return arr;
 };
diff --git a/src/lib/url-params.js b/src/lib/url-params.js
index f9b283608..f639f6e0f 100644
--- a/src/lib/url-params.js
+++ b/src/lib/url-params.js
@@ -2,21 +2,18 @@
  * urlParams({a: 1, b: 2, c: 3})
  * // a=1&b=2&c=3
  */
-module.exports = function urlParams (values) {
-    return Object
-        .keys(values)
-        .map(function (key) {
-            var value = typeof values[key] === 'undefined' ? '' : values[key];
-            function encodeKeyValuePair (value) {
-                return [key, value]
-                    .map(encodeURIComponent)
-                    .join('=');
-            }
-            if (Array.isArray(value)) {
-                return value.map(encodeKeyValuePair).join('&');
-            } else {
-                return encodeKeyValuePair(value);
-            }
-        })
-        .join('&');
-};
+const params = values => (
+    Object.keys(values).map(key => {
+        const value = typeof values[key] === 'undefined' ? '' : values[key];
+        const encodeKeyValuePair = val => (
+            [key, val].map(encodeURIComponent).join('=')
+        );
+        if (Array.isArray(value)) {
+            return value.map(encodeKeyValuePair).join('&');
+        }
+        return encodeKeyValuePair(value);
+    })
+        .join('&')
+);
+
+module.exports = params;
diff --git a/src/redux/conference-details.js b/src/redux/conference-details.js
index 0e3cf4b25..a9ac55447 100644
--- a/src/redux/conference-details.js
+++ b/src/redux/conference-details.js
@@ -1,13 +1,13 @@
-var keyMirror = require('keymirror');
-var api = require('../lib/api');
+const keyMirror = require('keymirror');
+const api = require('../lib/api');
 
-var Types = keyMirror({
+const Types = keyMirror({
     SET_DETAILS: null,
     SET_DETAILS_FETCHING: null,
     SET_DETAILS_ERROR: null
 });
 
-module.exports.detailsReducer = function (state, action) {
+module.exports.detailsReducer = (state, action) => {
     if (typeof state === 'undefined') {
         state = {};
     }
@@ -23,61 +23,50 @@ module.exports.detailsReducer = function (state, action) {
     }
 };
 
-module.exports.setDetailsError = function (error) {
-    return {
-        type: Types.SET_DETAILS_ERROR,
-        error: error
-    };
-};
+module.exports.setDetailsError = error => ({
+    type: Types.SET_DETAILS_ERROR,
+    error: error
+});
 
-module.exports.setDetails = function (details) {
-    return {
-        type: Types.SET_DETAILS,
-        details: details
-    };
-};
+module.exports.setDetails = details => ({
+    type: Types.SET_DETAILS,
+    details: details
+});
 
-module.exports.setDetailsFetching = function () {
-    return {
-        type: Types.SET_DETAILS_FETCHING,
-        fetching: true
-    };
-};
+module.exports.setDetailsFetching = () => ({
+    type: Types.SET_DETAILS_FETCHING,
+    fetching: true
+});
 
-module.exports.startGetDetails = function (id) {
-    return function (dispatch) {
-        dispatch(module.exports.setDetailsFetching());
-        dispatch(module.exports.getDetails(id));
-    };
-};
+module.exports.getDetails = id => (dispatch => {
+    api({
+        uri: `/conference/${id}/details`
+    }, (err, body) => {
+        if (err) {
+            dispatch(module.exports.setDetailsError(err));
+            return;
+        }
 
-module.exports.getDetails = function (id) {
-    return function (dispatch) {
-        api({
-            uri: '/conference/' + id + '/details'
-        }, function (err, body) {
-            if (err) {
-                dispatch(module.exports.setDetailsError(err));
-                return;
-            }
-
-            if (typeof body !== 'undefined') {
-                var columns = body.columns;
-                if (body.rows) {
-                    var details = body.rows[0];
-                    var detailsObject = details.reduce(function (prev, cur, index) {
-                        prev[columns[index]] = cur;
-                        return prev;
-                    }, {});
-                    dispatch(module.exports.setDetails(detailsObject));
-                } else {
-                    dispatch(module.exports.setDetailsError('Not Found'));
-                }
-                return;
+        if (typeof body !== 'undefined') {
+            const columns = body.columns;
+            if (body.rows) {
+                const details = body.rows[0];
+                const detailsObject = details.reduce((prev, cur, index) => {
+                    prev[columns[index]] = cur;
+                    return prev;
+                }, {});
+                dispatch(module.exports.setDetails(detailsObject));
             } else {
-                dispatch(module.exports.setDetailsError('An unexpected error occurred'));
-                return;
+                dispatch(module.exports.setDetailsError('Not Found'));
             }
-        });
-    };
-};
+            return;
+        }
+        dispatch(module.exports.setDetailsError('An unexpected error occurred'));
+        return;
+    });
+});
+
+module.exports.startGetDetails = id => (dispatch => {
+    dispatch(module.exports.setDetailsFetching());
+    dispatch(module.exports.getDetails(id));
+});
diff --git a/src/redux/conference-schedule.js b/src/redux/conference-schedule.js
index 4509f7b8e..224341c64 100644
--- a/src/redux/conference-schedule.js
+++ b/src/redux/conference-schedule.js
@@ -1,13 +1,13 @@
-var keyMirror = require('keymirror');
-var api = require('../lib/api');
+const keyMirror = require('keymirror');
+const api = require('../lib/api');
 
-var Types = keyMirror({
+const Types = keyMirror({
     SET_SCHEDULE: null,
     SET_SCHEDULE_FETCHING: null,
     SET_SCHEDULE_ERROR: null
 });
 
-module.exports.scheduleReducer = function (state, action) {
+module.exports.scheduleReducer = (state, action) => {
     if (typeof state === 'undefined') {
         state = {
             timeSlots: [],
@@ -26,40 +26,27 @@ module.exports.scheduleReducer = function (state, action) {
     }
 };
 
-module.exports.setSchedule = function (schedule) {
-    return {
-        type: Types.SET_SCHEDULE,
-        schedule: schedule
-    };
-};
+module.exports.setSchedule = schedule => ({
+    type: Types.SET_SCHEDULE,
+    schedule: schedule
+});
 
-module.exports.setScheduleFetching = function () {
-    return {
-        type: Types.SET_SCHEDULE_FETCHING,
-        fetching: true
-    };
-};
+module.exports.setScheduleFetching = () => ({
+    type: Types.SET_SCHEDULE_FETCHING,
+    fetching: true
+});
 
-module.exports.setScheduleError = function (error) {
-    return {
-        type: Types.SET_SCHEDULE_ERROR,
-        error: error
-    };
-};
-
-module.exports.startGetSchedule = function (day) {
-    return function (dispatch) {
-        dispatch(module.exports.setScheduleFetching());
-        dispatch(module.exports.getDaySchedule(day));
-    };
-};
+module.exports.setScheduleError = error => ({
+    type: Types.SET_SCHEDULE_ERROR,
+    error: error
+});
 
 // group periods of time by start time
-module.exports.sortTimeSlots = function (timeSlot1, timeSlot2) {
-    var timeSlot1Am = (timeSlot1.time.substr(timeSlot1.time.length - 1, timeSlot1.time.length) === 'a') ? true : false;
-    var timeSlot2Am = (timeSlot2.time.substr(timeSlot2.time.length - 1, timeSlot2.time.length) === 'a') ? true : false;
-    var timeSlot1Time = parseInt(timeSlot1.time.substr(0, timeSlot1.time.length - 1));
-    var timeSlot2Time = parseInt(timeSlot2.time.substr(0, timeSlot2.time.length - 1));
+module.exports.sortTimeSlots = (timeSlot1, timeSlot2) => {
+    const timeSlot1Am = (timeSlot1.time.substr(timeSlot1.time.length - 1, timeSlot1.time.length) === 'a');
+    const timeSlot2Am = (timeSlot2.time.substr(timeSlot2.time.length - 1, timeSlot2.time.length) === 'a');
+    let timeSlot1Time = parseInt(timeSlot1.time.substr(0, timeSlot1.time.length - 1), 10);
+    let timeSlot2Time = parseInt(timeSlot2.time.substr(0, timeSlot2.time.length - 1), 10);
     
     // convert to 24-hour for sorting
     if (timeSlot1Time !== 12 && !timeSlot1Am) {
@@ -71,68 +58,67 @@ module.exports.sortTimeSlots = function (timeSlot1, timeSlot2) {
     
     if (timeSlot1Time < timeSlot2Time) {
         return -1;
-    } else {
-        return 1;
     }
+    return 1;
 };
 
 /**
  * Gets the schedule for the given day from the api
- * @param  {String} day  Day of the conference (Thursday, Friday or Satrurday)
+ * @param  {string} day  Day of the conference (Thursday, Friday or Satrurday)
  *
- *  @return {Object}     Schedule for the day, broken into chunks
+ *  @return {object}     Schedule for the day, broken into chunks
  */
-module.exports.getDaySchedule = function (day) {
-    return function (dispatch) {
-        api({
-            uri: '/conference/schedule/' + day
-        }, function (err, body) {
-            if (err) {
-                dispatch(module.exports.setScheduleError(err));
-                return;
-            }
+module.exports.getDaySchedule = day => (dispatch => {
+    api({
+        uri: `/conference/schedule/${day}`
+    }, (err, body) => {
+        if (err) {
+            dispatch(module.exports.setScheduleError(err));
+            return;
+        }
 
-            if (typeof body !== 'undefined') {
-                var columns = body.columns;
-                var rows = body.rows || [];
-                // Group events by the time period in which they occur (for presentation)
-                var scheduleByTimeSlot = rows.reduce(function (prev, cur) {
-                    var cleanedRow = {};
-                    for (var i = 0; i < columns.length; i++) {
-                        if (cur[i].length > 0) {
-                            cleanedRow[columns[i]] = cur[i];
-                        }
+        if (typeof body !== 'undefined') {
+            const columns = body.columns;
+            const rows = body.rows || [];
+            // Group events by the time period in which they occur (for presentation)
+            const scheduleByTimeSlot = rows.reduce((prev, cur) => {
+                const cleanedRow = {};
+                for (let i = 0; i < columns.length; i++) {
+                    if (cur[i].length > 0) {
+                        cleanedRow[columns[i]] = cur[i];
                     }
-                    cleanedRow['uri'] = '/conference/2016/' + cleanedRow.rowid + '/details';
-                    var timeSlot = cleanedRow.Chunk + cleanedRow.Start;
-                    if (typeof prev.timeSlots[timeSlot] === 'undefined') {
-                        prev.timeSlots[timeSlot] = [cleanedRow];
-                        prev.info.push({
-                            name: cleanedRow.Chunk,
-                            time: cleanedRow.Start
-                        });
-                    } else {
-                        prev.timeSlots[timeSlot].push(cleanedRow);
-                    }
-                    return prev;
-                }, {timeSlots: [], info: []});
+                }
+                cleanedRow.uri = `/conference/2016/${cleanedRow.rowid}/details`;
+                const timeSlot = cleanedRow.Chunk + cleanedRow.Start;
+                if (typeof prev.timeSlots[timeSlot] === 'undefined') {
+                    prev.timeSlots[timeSlot] = [cleanedRow];
+                    prev.info.push({
+                        name: cleanedRow.Chunk,
+                        time: cleanedRow.Start
+                    });
+                } else {
+                    prev.timeSlots[timeSlot].push(cleanedRow);
+                }
+                return prev;
+            }, {timeSlots: [], info: []});
 
-                scheduleByTimeSlot.info.sort(module.exports.sortTimeSlots);
-                var schedule = scheduleByTimeSlot.info.map(function (timeSlot) {
-                    return {
-                        info: timeSlot,
-                        items: scheduleByTimeSlot.timeSlots[timeSlot.name + timeSlot.time]
-                    };
-                });
-                dispatch(module.exports.setSchedule({
-                    timeSlots: schedule,
-                    day: day
-                }));
-                return;
-            } else {
-                dispatch(module.exports.setScheduleError('An unexpected error occurred'));
-                return;
-            }
-        });
-    };
-};
+            scheduleByTimeSlot.info.sort(module.exports.sortTimeSlots);
+            const schedule = scheduleByTimeSlot.info.map(timeSlot => ({
+                info: timeSlot,
+                items: scheduleByTimeSlot.timeSlots[timeSlot.name + timeSlot.time]
+            }));
+            dispatch(module.exports.setSchedule({
+                timeSlots: schedule,
+                day: day
+            }));
+            return;
+        }
+        dispatch(module.exports.setScheduleError('An unexpected error occurred'));
+        return;
+    });
+});
+
+module.exports.startGetSchedule = day => (dispatch => {
+    dispatch(module.exports.setScheduleFetching());
+    dispatch(module.exports.getDaySchedule(day));
+});
diff --git a/src/redux/message-count.js b/src/redux/message-count.js
index 0efa84025..e3df74b33 100644
--- a/src/redux/message-count.js
+++ b/src/redux/message-count.js
@@ -1,22 +1,22 @@
-var keyMirror = require('keymirror');
-var defaults = require('lodash.defaults');
+const keyMirror = require('keymirror');
+const defaults = require('lodash.defaults');
 
-var api = require('../lib/api');
+const api = require('../lib/api');
 
-var Types = keyMirror({
+const Types = keyMirror({
     SET_MESSAGE_COUNT: null,
     SET_MESSAGE_COUNT_ERROR: null,
     SET_STATUS: null
 });
 
-module.exports.getInitialState = function (){
-    return {messageCount: 0};
-};
+const getInitialState = () => ({
+    messageCount: 0
+});
 
-module.exports.messageCountReducer = function (state, action) {
+module.exports.messageCountReducer = (state, action) => {
     // Reducer for handling changes to session state
     if (typeof state === 'undefined') {
-        state = module.exports.getInitialState();
+        state = getInitialState();
     }
     switch (action.type) {
     case Types.SET_MESSAGE_COUNT:
@@ -31,40 +31,32 @@ module.exports.messageCountReducer = function (state, action) {
     }
 };
 
-module.exports.setSessionError = function (error) {
-    return {
-        type: Types.SET_MESSAGE_COUNT_ERROR,
-        error: error
-    };
-};
+module.exports.setSessionError = error => ({
+    type: Types.SET_MESSAGE_COUNT_ERROR,
+    error: error
+});
 
-module.exports.setCount = function (count) {
-    return {
-        type: Types.SET_MESSAGE_COUNT,
-        count: count
-    };
-};
+module.exports.setCount = count => ({
+    type: Types.SET_MESSAGE_COUNT,
+    count: count
+});
 
-module.exports.setStatus = function (status){
-    return {
-        type: Types.SET_STATUS,
-        status: status
-    };
-};
+module.exports.setStatus = status => ({
+    type: Types.SET_STATUS,
+    status: status
+});
 
-module.exports.getCount = function (username) {
-    return function (dispatch) {
-        api({
-            method: 'get',
-            uri: '/users/' + username + '/messages/count'
-        }, function (err, body) {
-            if (err) {
-                dispatch(module.exports.setCount(0));
-                dispatch(module.exports.setSessionError(err));
-                return;
-            }
-            var count = parseInt(body.count, 10);
-            dispatch(module.exports.setCount(count));
-        });
-    };
-};
+module.exports.getCount = username => (dispatch => {
+    api({
+        method: 'get',
+        uri: `/users/${username}/messages/count`
+    }, (err, body) => {
+        if (err) {
+            dispatch(module.exports.setCount(0));
+            dispatch(module.exports.setSessionError(err));
+            return;
+        }
+        const count = parseInt(body.count, 10);
+        dispatch(module.exports.setCount(count));
+    });
+});
diff --git a/src/redux/messages.js b/src/redux/messages.js
index d976162f5..7cc7e4238 100644
--- a/src/redux/messages.js
+++ b/src/redux/messages.js
@@ -1,10 +1,10 @@
-var defaults = require('lodash.defaults');
-var defaultsDeep = require('lodash.defaultsdeep');
-var keyMirror = require('keymirror');
+const defaults = require('lodash.defaults');
+const defaultsDeep = require('lodash.defaultsdeep');
+const keyMirror = require('keymirror');
 
-var api = require('../lib/api');
-var log = require('../lib/log');
-var messageCountActions = require('./message-count.js');
+const api = require('../lib/api');
+const log = require('../lib/log');
+const messageCountActions = require('./message-count.js');
 
 module.exports.Status = keyMirror({
     FETCHED: null,
@@ -17,23 +17,21 @@ module.exports.Status = keyMirror({
     DELETE_ERROR: null
 });
 
-module.exports.getInitialState = function () {
-    return {
-        status: {
-            admin: module.exports.Status.NOT_FETCHED,
-            message: module.exports.Status.NOT_FETCHED,
-            clear: module.exports.Status.NOT_FETCHED,
-            delete: module.exports.Status.NOT_FETCHED
-        },
-        messages: {
-            admin: [],
-            social: [],
-            invite: {}
-        }
-    };
-};
+module.exports.getInitialState = () => ({
+    status: {
+        admin: module.exports.Status.NOT_FETCHED,
+        message: module.exports.Status.NOT_FETCHED,
+        clear: module.exports.Status.NOT_FETCHED,
+        delete: module.exports.Status.NOT_FETCHED
+    },
+    messages: {
+        admin: [],
+        social: [],
+        invite: {}
+    }
+});
 
-module.exports.messagesReducer = function (state, action) {
+module.exports.messagesReducer = (state, action) => {
     if (typeof state === 'undefined') {
         state = module.exports.getInitialState();
     }
@@ -68,75 +66,61 @@ module.exports.messagesReducer = function (state, action) {
     }
 };
 
-module.exports.setMessagesError = function (error) {
-    return {
-        type: 'ERROR',
-        error: error
-    };
-};
+module.exports.setMessagesError = error => ({
+    type: 'ERROR',
+    error: error
+});
 
-module.exports.setMessages = function (messages) {
-    return {
-        type: 'SET_MESSAGES',
-        messages: messages
-    };
-};
+module.exports.setMessages = messages => ({
+    type: 'SET_MESSAGES',
+    messages: messages
+});
 
-module.exports.setMessagesOffset = function (offset) {
-    return {
-        type: 'SET_MESSAGES_OFFSET',
-        offset: offset
-    };
-};
+module.exports.setMessagesOffset = offset => ({
+    type: 'SET_MESSAGES_OFFSET',
+    offset: offset
+});
 
-module.exports.setAdminMessages = function (messages) {
-    return {
-        type: 'SET_ADMIN_MESSAGES',
-        messages: messages
-    };
-};
+module.exports.setAdminMessages = messages => ({
+    type: 'SET_ADMIN_MESSAGES',
+    messages: messages
+});
 
-module.exports.setScratcherInvite = function (invite) {
-    return {
-        type: 'SET_SCRATCHER_INVITE',
-        invite: invite
-    };
-};
+module.exports.setScratcherInvite = invite => ({
+    type: 'SET_SCRATCHER_INVITE',
+    invite: invite
+});
 
-module.exports.setStatus = function (type, status){
-    return {
-        type: type,
-        status: status
-    };
-};
+module.exports.setStatus = (type, status) => ({
+    type: type,
+    status: status
+});
 
 /**
  * Sends a request to mark one's unread messages count as cleared.
  * @return {null} returns nothing
  */
-module.exports.clearMessageCount = function () {
-    return function (dispatch) {
-        dispatch(module.exports.setStatus('CLEAR_STATUS', module.exports.Status.FETCHING));
-        api({
-            host: '',
-            uri: '/site-api/messages/messages-clear/',
-            method: 'POST',
-            useCsrf: true
-        }, function (err, body) {
-            if (err) {
-                dispatch(module.exports.setStatus('CLEAR_STATUS', module.exports.Status.CLEAR_ERROR));
-                dispatch(module.exports.setMessagesError(err));
-                return;
-            }
-            if (typeof body !== 'undefined' && !body.success) {
-                dispatch(module.exports.setStatus('CLEAR_STATUS', module.exports.Status.CLEAR_ERROR));
-                dispatch(module.exports.setMessagesError('messages not cleared'));
-                return;
-            }
-            dispatch(module.exports.setStatus('CLEAR_STATUS', module.exports.Status.FETCHED));
-        });
-    };
-};
+module.exports.clearMessageCount = () => (dispatch => {
+    dispatch(module.exports.setStatus('CLEAR_STATUS', module.exports.Status.FETCHING));
+    api({
+        host: '',
+        uri: '/site-api/messages/messages-clear/',
+        method: 'POST',
+        useCsrf: true
+    }, (err, body) => {
+        if (err) {
+            dispatch(module.exports.setStatus('CLEAR_STATUS', module.exports.Status.CLEAR_ERROR));
+            dispatch(module.exports.setMessagesError(err));
+            return;
+        }
+        if (typeof body !== 'undefined' && !body.success) {
+            dispatch(module.exports.setStatus('CLEAR_STATUS', module.exports.Status.CLEAR_ERROR));
+            dispatch(module.exports.setMessagesError('messages not cleared'));
+            return;
+        }
+        dispatch(module.exports.setStatus('CLEAR_STATUS', module.exports.Status.FETCHED));
+    });
+});
 
 /**
  * Marks an admin message as read, dismissing it from the page
@@ -146,8 +130,8 @@ module.exports.clearMessageCount = function () {
  * @param  {object[]} adminMessages current list of admin messages retrieved
  * @return {null}                   returns nothing
  */
-module.exports.clearAdminMessage = function (messageType, messageId, messageCount, adminMessages) {
-    return function (dispatch) {
+module.exports.clearAdminMessage = (messageType, messageId, messageCount, adminMessages) => (
+    dispatch => {
         dispatch(module.exports.setStatus('CLEAR_STATUS', module.exports.Status.FETCHING));
         api({
             host: '',
@@ -158,7 +142,7 @@ module.exports.clearAdminMessage = function (messageType, messageId, messageCoun
                 alertType: messageType,
                 alertId: messageId
             }
-        }, function (err, body) {
+        }, (err, body) => {
             if (err) {
                 dispatch(module.exports.setStatus('DELETE_STATUS', module.exports.Status.DELETE_ERROR));
                 dispatch(module.exports.setMessagesError(err));
@@ -175,9 +159,9 @@ module.exports.clearAdminMessage = function (messageType, messageId, messageCoun
                 dispatch(module.exports.setScratcherInvite({}));
             } else {
                 // find the admin message and remove it
-                var toRemove = -1;
-                for (var i in adminMessages) {
-                    if (adminMessages[i].id === messageId) {
+                let toRemove = -1;
+                for (const i of adminMessages) {
+                    if (i.id === messageId) {
                         toRemove = i;
                         break;
                     }
@@ -188,8 +172,8 @@ module.exports.clearAdminMessage = function (messageType, messageId, messageCoun
             dispatch(messageCountActions.setCount(messageCount - 1));
             dispatch(module.exports.setStatus('DELETE_STATUS', module.exports.Status.FETCHED));
         });
-    };
-};
+    }
+);
 
 /**
  * Gets a user's messages to be displayed on the /messages page
@@ -201,7 +185,7 @@ module.exports.clearAdminMessage = function (messageType, messageId, messageCoun
  * @param  {string}   [opts.filter]      type of messages to return
  * @return {null}                     returns nothing
  */
-module.exports.getMessages = function (username, token, opts) {
+module.exports.getMessages = (username, token, opts) => {
     opts = defaults(opts, {
         messages: [],
         offset: 0,
@@ -209,17 +193,17 @@ module.exports.getMessages = function (username, token, opts) {
         clearCount: true
     });
 
-    var filterArg = '';
+    let filterArg = '';
     if (opts.filter.length > 0) {
-        filterArg = '&filter=' + opts.filter;
+        filterArg = `&filter=${opts.filter}`;
     }
 
-    return function (dispatch) {
+    return dispatch => {
         dispatch(module.exports.setStatus('MESSAGE_STATUS', module.exports.Status.FETCHING));
         api({
-            uri: '/users/' + username + '/messages?limit=40&offset=' + opts.offset + filterArg,
+            uri: `/users/${username}/messages?limit=40&offset=${opts.offset}${filterArg}`,
             authentication: token
-        }, function (err, body) {
+        }, (err, body) => {
             if (err) {
                 dispatch(module.exports.setStatus('MESSAGE_STATUS', module.exports.Status.MESSAGES_ERROR));
                 dispatch(module.exports.setMessagesError(err));
@@ -246,30 +230,28 @@ module.exports.getMessages = function (username, token, opts) {
  * @param  {string} token    the user's unique token for auth
  * @return {null}            returns nothing
  */
-module.exports.getAdminMessages = function (username, token) {
-    return function (dispatch) {
-        dispatch(module.exports.setStatus('ADMIN_STATUS', module.exports.Status.FETCHING));
-        api({
-            uri: '/users/' + username + '/messages/admin',
-            authentication: token
-        }, function (err, body) {
-            if (err) {
-                dispatch(module.exports.setStatus('ADMIN_STATUS', module.exports.Status.ADMIN_ERROR));
-                dispatch(module.exports.setMessagesError(err));
-                dispatch(module.exports.setAdminMessages([]));
-                return;
-            }
-            if (typeof body === 'undefined') {
-                dispatch(module.exports.setStatus('ADMIN_STATUS', module.exports.Status.ADMIN_ERROR));
-                dispatch(module.exports.setMessagesError('No session content'));
-                dispatch(module.exports.setAdminMessages([]));
-                return;
-            }
-            dispatch(module.exports.setAdminMessages(body));
-            dispatch(module.exports.setStatus('ADMIN_STATUS', module.exports.Status.FETCHED));
-        });
-    };
-};
+module.exports.getAdminMessages = (username, token) => (dispatch => {
+    dispatch(module.exports.setStatus('ADMIN_STATUS', module.exports.Status.FETCHING));
+    api({
+        uri: `/users/${username}/messages/admin`,
+        authentication: token
+    }, (err, body) => {
+        if (err) {
+            dispatch(module.exports.setStatus('ADMIN_STATUS', module.exports.Status.ADMIN_ERROR));
+            dispatch(module.exports.setMessagesError(err));
+            dispatch(module.exports.setAdminMessages([]));
+            return;
+        }
+        if (typeof body === 'undefined') {
+            dispatch(module.exports.setStatus('ADMIN_STATUS', module.exports.Status.ADMIN_ERROR));
+            dispatch(module.exports.setMessagesError('No session content'));
+            dispatch(module.exports.setAdminMessages([]));
+            return;
+        }
+        dispatch(module.exports.setAdminMessages(body));
+        dispatch(module.exports.setStatus('ADMIN_STATUS', module.exports.Status.FETCHED));
+    });
+});
 
 /**
  * Gets the invitation to become a Scratcher for a user, if one exists
@@ -277,20 +259,18 @@ module.exports.getAdminMessages = function (username, token) {
  * @param  {string} token    the user's unique token for auth
  * @return {null}            returns nothing
  */
-module.exports.getScratcherInvite = function (username, token) {
-    return function (dispatch) {
-        api({
-            uri: '/users/' + username + '/invites',
-            authentication: token
-        }, function (err, body) {
-            if (err) {
-                dispatch(module.exports.setStatus('ADMIN_STATUS', module.exports.Status.INVITE_ERROR));
-                dispatch(module.exports.setMessagesError(err));
-                dispatch(module.exports.setScratcherInvite({}));
-                return;
-            }
-            if (typeof body === 'undefined') return dispatch(module.exports.setMessagesError('No session content'));
-            dispatch(module.exports.setScratcherInvite(body));
-        });
-    };
-};
+module.exports.getScratcherInvite = (username, token) => (dispatch => {
+    api({
+        uri: `/users/${username}/invites`,
+        authentication: token
+    }, (err, body) => {
+        if (err) {
+            dispatch(module.exports.setStatus('ADMIN_STATUS', module.exports.Status.INVITE_ERROR));
+            dispatch(module.exports.setMessagesError(err));
+            dispatch(module.exports.setScratcherInvite({}));
+            return;
+        }
+        if (typeof body === 'undefined') return dispatch(module.exports.setMessagesError('No session content'));
+        dispatch(module.exports.setScratcherInvite(body));
+    });
+});
diff --git a/src/redux/navigation.js b/src/redux/navigation.js
index 5ced2f06e..088fc7fb6 100644
--- a/src/redux/navigation.js
+++ b/src/redux/navigation.js
@@ -1,11 +1,11 @@
-var keyMirror = require('keymirror');
+const keyMirror = require('keymirror');
 
-var Types = keyMirror({
+const Types = keyMirror({
     SET_SEARCH_TERM: null
 });
 
-module.exports.navigationReducer = function (state, action) {
-    if(typeof state === 'undefined') {
+module.exports.navigationReducer = (state, action) => {
+    if (typeof state === 'undefined') {
         state = '';
     }
     switch (action.type) {
@@ -16,9 +16,7 @@ module.exports.navigationReducer = function (state, action) {
     }
 };
 
-module.exports.setSearchTerm = function (searchTerm) {
-    return {
-        type: Types.SET_SEARCH_TERM,
-        searchTerm: searchTerm
-    };
-};
+module.exports.setSearchTerm = searchTerm => ({
+    type: Types.SET_SEARCH_TERM,
+    searchTerm: searchTerm
+});
diff --git a/src/redux/permissions.js b/src/redux/permissions.js
index 0530cb868..87eebf64e 100644
--- a/src/redux/permissions.js
+++ b/src/redux/permissions.js
@@ -1,14 +1,14 @@
-var keyMirror = require('keymirror');
-var jar = require('../lib/jar.js');
+const keyMirror = require('keymirror');
+const jar = require('../lib/jar.js');
 
-var Types = keyMirror({
+const Types = keyMirror({
     SET_PERMISSIONS: null,
     SET_PERMISSIONS_ERROR: null
 });
 
-module.exports.permissionsReducer = function (state, action) {
+module.exports.permissionsReducer = (state, action) => {
     if (typeof state === 'undefined') {
-        state = '';
+        state = {};
     }
     switch (action.type) {
     case Types.SET_PERMISSIONS:
@@ -20,43 +20,37 @@ module.exports.permissionsReducer = function (state, action) {
     }
 };
 
-module.exports.storePermissions = function (permissions) {
+module.exports.storePermissions = permissions => {
     permissions = permissions || {};
-    return function (dispatch) {
+    return dispatch => {
         jar.set('permissions', permissions, {
-            encode: function (value) {
-                return encodeURIComponent(JSON.stringify(value));
-            }
+            encode: value => (
+                encodeURIComponent(JSON.stringify(value))
+            )
         });
         return dispatch(module.exports.setPermissions(permissions));
     };
 };
 
-module.exports.getPermissions = function () {
-    return function (dispatch) {
-        jar.get('permissions', function (err, value) {
-            if (err) return dispatch(module.exports.setPermissionsError(err));
+module.exports.getPermissions = () => (dispatch => {
+    jar.get('permissions', (err, value) => {
+        if (err) return dispatch(module.exports.setPermissionsError(err));
 
-            try {
-                value = JSON.parse(decodeURIComponent(value)) || {};
-            } catch (e) {
-                value = {};
-            }
-            return dispatch(module.exports.setPermissions(value));
-        });
-    };
-};
+        try {
+            value = JSON.parse(decodeURIComponent(value)) || {};
+        } catch (e) {
+            value = {};
+        }
+        return dispatch(module.exports.setPermissions(value));
+    });
+});
 
-module.exports.setPermissions = function (permissions) {
-    return {
-        type: Types.SET_PERMISSIONS,
-        permissions: permissions
-    };
-};
+module.exports.setPermissions = permissions => ({
+    type: Types.SET_PERMISSIONS,
+    permissions: permissions
+});
 
-module.exports.setPermissionsError = function (error) {
-    return {
-        type: Types.SET_PERMISSIONS_ERROR,
-        error: error
-    };
-};
+module.exports.setPermissionsError = error => ({
+    type: Types.SET_PERMISSIONS_ERROR,
+    error: error
+});
diff --git a/src/redux/reducer.js b/src/redux/reducer.js
index 951b03a8a..3df093a98 100644
--- a/src/redux/reducer.js
+++ b/src/redux/reducer.js
@@ -1,9 +1,9 @@
-var combineReducers = require('redux').combineReducers;
-var defaults = require('lodash.defaults');
+const combineReducers = require('redux').combineReducers;
+const defaults = require('lodash.defaults');
 
-var messageCountReducer = require('./message-count.js').messageCountReducer;
-var permissionsReducer = require('./permissions.js').permissionsReducer;
-var sessionReducer = require('./session.js').sessionReducer;
+const messageCountReducer = require('./message-count.js').messageCountReducer;
+const permissionsReducer = require('./permissions.js').permissionsReducer;
+const sessionReducer = require('./session.js').sessionReducer;
 
 /**
  * Returns a combined reducer to be used for a page in `render.jsx`.
@@ -11,11 +11,11 @@ var sessionReducer = require('./session.js').sessionReducer;
  * - and any reducers specific to the page should be passed into
  * `render()` as an object (which will then be passed to the function
  * below).
- * @param  {Object} opts key/value where the key is the name of the
+ * @param  {object} opts key/value where the key is the name of the
  *                       redux state, value is the reducer function.
- * @return {Object}      combined reducer to be used in the redux store
+ * @return {object}      combined reducer to be used in the redux store
  */
-module.exports = function (opts) {
+module.exports = opts => {
     opts = opts || {};
     return combineReducers(defaults(opts, {
         session: sessionReducer,
diff --git a/src/redux/session.js b/src/redux/session.js
index 12645753f..d5701cb1d 100644
--- a/src/redux/session.js
+++ b/src/redux/session.js
@@ -1,17 +1,17 @@
-var keyMirror = require('keymirror');
-var defaults = require('lodash.defaults');
+const keyMirror = require('keymirror');
+const defaults = require('lodash.defaults');
 
-var api = require('../lib/api');
-var messageCountActions = require('./message-count.js');
-var permissionsActions = require('./permissions.js');
+const api = require('../lib/api');
+const messageCountActions = require('./message-count.js');
+const permissionsActions = require('./permissions.js');
 
-var Types = keyMirror({
+const Types = keyMirror({
     SET_SESSION: null,
     SET_SESSION_ERROR: null,
     SET_STATUS: null
 });
 
-var banWhitelistPaths = [
+const banWhitelistPaths = [
     '/accounts/banned-response/',
     '/community_guidelines/',
     '/community_guidelines'
@@ -23,11 +23,12 @@ module.exports.Status = keyMirror({
     FETCHING: null
 });
 
-module.exports.getInitialState = function (){
-    return {status: module.exports.Status.NOT_FETCHED, session:{}};
-};
+module.exports.getInitialState = () => ({
+    status: module.exports.Status.NOT_FETCHED,
+    session: {}
+});
 
-module.exports.sessionReducer = function (state, action) {
+module.exports.sessionReducer = (state, action) => {
     // Reducer for handling changes to session state
     if (typeof state === 'undefined') {
         state = module.exports.getInitialState();
@@ -45,63 +46,60 @@ module.exports.sessionReducer = function (state, action) {
     }
 };
 
-module.exports.setSessionError = function (error) {
-    return {
-        type: Types.SET_SESSION_ERROR,
-        error: error
-    };
-};
+module.exports.setSessionError = error => ({
+    type: Types.SET_SESSION_ERROR,
+    error: error
+});
 
-module.exports.setSession = function (session) {
-    return {
-        type: Types.SET_SESSION,
-        session: session
-    };
-};
+module.exports.setSession = session => ({
+    type: Types.SET_SESSION,
+    session: session
+});
 
-module.exports.setStatus = function (status){
-    return {
-        type: Types.SET_STATUS,
-        status: status
-    };
-};
+module.exports.setStatus = status => ({
+    type: Types.SET_STATUS,
+    status: status
+});
 
-module.exports.refreshSession = function () {
-    return function (dispatch) {
-        dispatch(module.exports.setStatus(module.exports.Status.FETCHING));
-        api({
-            host: '',
-            uri: '/session/'
-        }, function (err, body) {
-            if (err) return dispatch(module.exports.setSessionError(err));
-            if (typeof body === 'undefined') return dispatch(module.exports.setSessionError('No session content'));
-            if (
-                    body.user &&
-                    body.user.banned &&
-                    banWhitelistPaths.indexOf(window.location.pathname) === -1) {
-                return window.location = '/accounts/banned-response/';
-            } else if (
-                    body.flags &&
-                    body.flags.must_complete_registration &&
-                    window.location.pathname !== '/classes/complete_registration') {
-                return window.location = '/classes/complete_registration';
-            } else if (
-                    body.flags &&
-                    body.flags.must_reset_password &&
-                    !body.flags.must_complete_registration &&
-                    window.location.pathname !== '/classes/student_password_reset/') {
-                return window.location = '/classes/student_password_reset/';
-            } else {
-                dispatch(module.exports.setSession(body));
-                dispatch(module.exports.setStatus(module.exports.Status.FETCHED));
+module.exports.refreshSession = () => (dispatch => {
+    dispatch(module.exports.setStatus(module.exports.Status.FETCHING));
+    api({
+        host: '',
+        uri: '/session/'
+    }, (err, body) => {
+        if (err) return dispatch(module.exports.setSessionError(err));
+        if (typeof body === 'undefined') return dispatch(module.exports.setSessionError('No session content'));
+        if (
+            body.user &&
+            body.user.banned &&
+            banWhitelistPaths.indexOf(window.location.pathname) === -1
+        ) {
+            window.location = '/accounts/banned-response/';
+            return;
+        } else if (
+            body.flags &&
+            body.flags.must_complete_registration &&
+            window.location.pathname !== '/classes/complete_registration'
+        ) {
+            window.location = '/classes/complete_registration';
+            return;
+        } else if (
+            body.flags &&
+            body.flags.must_reset_password &&
+            !body.flags.must_complete_registration &&
+            window.location.pathname !== '/classes/student_password_reset/'
+        ) {
+            window.location = '/classes/student_password_reset/';
+            return;
+        }
+        dispatch(module.exports.setSession(body));
+        dispatch(module.exports.setStatus(module.exports.Status.FETCHED));
 
-                // get the permissions from the updated session
-                dispatch(permissionsActions.storePermissions(body.permissions));
-                if (typeof body.user !== 'undefined') {
-                    dispatch(messageCountActions.getCount(body.user.username));
-                }
-                return;
-            }
-        });
-    };
-};
+        // get the permissions from the updated session
+        dispatch(permissionsActions.storePermissions(body.permissions));
+        if (typeof body.user !== 'undefined') {
+            dispatch(messageCountActions.getCount(body.user.username));
+        }
+        return;
+    });
+});
diff --git a/src/redux/splash.js b/src/redux/splash.js
index 6a02c5c8d..65840ddec 100644
--- a/src/redux/splash.js
+++ b/src/redux/splash.js
@@ -1,7 +1,7 @@
-var keyMirror = require('keymirror');
+const keyMirror = require('keymirror');
 
-var api = require('../lib/api');
-var log = require('../lib/log');
+const api = require('../lib/api');
+const log = require('../lib/log');
 
 module.exports.Status = keyMirror({
     FETCHED: null,
@@ -10,32 +10,30 @@ module.exports.Status = keyMirror({
     ERROR: null
 });
 
-module.exports.getInitialState = function () {
-    return {
-        activity: {
-            status: module.exports.Status.NOT_FETCHED,
-            rows: []
-        },
-        featured: {
-            status: module.exports.Status.NOT_FETCHED,
-            rows: {}
-        },
-        shared: {
-            status: module.exports.Status.NOT_FETCHED,
-            rows: []
-        },
-        loved: {
-            status: module.exports.Status.NOT_FETCHED,
-            rows: []
-        },
-        studios: {
-            status: module.exports.Status.NOT_FETCHED,
-            rows: []
-        }
-    };
-};
+module.exports.getInitialState = () => ({
+    activity: {
+        status: module.exports.Status.NOT_FETCHED,
+        rows: []
+    },
+    featured: {
+        status: module.exports.Status.NOT_FETCHED,
+        rows: {}
+    },
+    shared: {
+        status: module.exports.Status.NOT_FETCHED,
+        rows: []
+    },
+    loved: {
+        status: module.exports.Status.NOT_FETCHED,
+        rows: []
+    },
+    studios: {
+        status: module.exports.Status.NOT_FETCHED,
+        rows: []
+    }
+});
 
-module.exports.splashReducer = function (state, action) {
+module.exports.splashReducer = (state, action) => {
     if (typeof state === 'undefined') {
         state = module.exports.getInitialState();
     }
@@ -57,157 +55,141 @@ module.exports.splashReducer = function (state, action) {
     }
 };
 
-module.exports.setError = function (error) {
-    return {
-        type: 'ERROR',
-        error: error
-    };
-};
+module.exports.setError = error => ({
+    type: 'ERROR',
+    error: error
+});
 
-module.exports.setRows = function (type, rows) {
-    return {
-        type: 'SET_ROWS',
-        rowType: type,
-        rows: rows
-    };
-};
+module.exports.setRows = (type, rows) => ({
+    type: 'SET_ROWS',
+    rowType: type,
+    rows: rows
+});
 
-module.exports.setFetchStatus = function (type, status){
-    return {
-        type: 'SET_FETCH_STATUS',
-        rowType: type,
-        status: status
-    };
-};
+module.exports.setFetchStatus = (type, status) => ({
+    type: 'SET_FETCH_STATUS',
+    rowType: type,
+    status: status
+});
 
-module.exports.getActivity = function (username, token) {
-    return function (dispatch) {
-        dispatch(module.exports.setFetchStatus('activity', module.exports.Status.FETCHING));
-        api({
-            uri: '/users/' + username + '/following/users/activity?limit=5',
-            authentication: token
-        }, function (err, body) {
-            if (err) {
-                dispatch(module.exports.setFetchStatus('activity', module.exports.Status.ERROR));
-                dispatch(module.exports.setError(err));
-                return;
-            }
-            if (typeof body === 'undefined') {
-                dispatch(module.exports.setFetchStatus('activity', module.exports.Status.ERROR));
-                dispatch(module.exports.setError('No session content'));
-                return;
-            }
-            dispatch(module.exports.setFetchStatus('activity', module.exports.Status.FETCHED));
-            dispatch(module.exports.setRows('activity', body));
-        });
-    };
-};
+module.exports.getActivity = (username, token) => (dispatch => {
+    dispatch(module.exports.setFetchStatus('activity', module.exports.Status.FETCHING));
+    api({
+        uri: `/users/${username}/following/users/activity?limit=5`,
+        authentication: token
+    }, (err, body) => {
+        if (err) {
+            dispatch(module.exports.setFetchStatus('activity', module.exports.Status.ERROR));
+            dispatch(module.exports.setError(err));
+            return;
+        }
+        if (typeof body === 'undefined') {
+            dispatch(module.exports.setFetchStatus('activity', module.exports.Status.ERROR));
+            dispatch(module.exports.setError('No session content'));
+            return;
+        }
+        dispatch(module.exports.setFetchStatus('activity', module.exports.Status.FETCHED));
+        dispatch(module.exports.setRows('activity', body));
+    });
+});
 
-/**
+/*
  * Get global homepage rows
  */
-module.exports.getFeaturedGlobal = function () {
-    return function (dispatch) {
-        dispatch(module.exports.setFetchStatus('featured', module.exports.Status.FETCHING));
-        api({
-            uri: '/proxy/featured'
-        }, function (err, body) {
-            if (err) {
-                dispatch(module.exports.setFetchStatus('featured', module.exports.Status.ERROR));
-                dispatch(module.exports.setError(err));
-                return;
-            }
-            if (typeof body === 'undefined') {
-                dispatch(module.exports.setFetchStatus('featured', module.exports.Status.ERROR));
-                dispatch(module.exports.setError('No session content'));
-                return;
-            }
-            dispatch(module.exports.setFetchStatus('featured', module.exports.Status.FETCHED));
-            dispatch(module.exports.setRows('featured', body));
-        });
-    };
-};
+module.exports.getFeaturedGlobal = () => (dispatch => {
+    dispatch(module.exports.setFetchStatus('featured', module.exports.Status.FETCHING));
+    api({
+        uri: '/proxy/featured'
+    }, (err, body) => {
+        if (err) {
+            dispatch(module.exports.setFetchStatus('featured', module.exports.Status.ERROR));
+            dispatch(module.exports.setError(err));
+            return;
+        }
+        if (typeof body === 'undefined') {
+            dispatch(module.exports.setFetchStatus('featured', module.exports.Status.ERROR));
+            dispatch(module.exports.setError('No session content'));
+            return;
+        }
+        dispatch(module.exports.setFetchStatus('featured', module.exports.Status.FETCHED));
+        dispatch(module.exports.setRows('featured', body));
+    });
+});
 
-/**
+/*
  * Get list of projects shared by users the given user is following.
  * @param  {string} username username of the Scratcher for whom to get projects
  * @param  {string} token    authentication
  */
-module.exports.getSharedByFollowing = function (username, token) {
-    return function (dispatch) {
-        dispatch(module.exports.setFetchStatus('shared', module.exports.Status.FETCHING));
-        api({
-            uri: '/users/' + username + '/following/users/projects',
-            authentication: token
-        }, function (err, body) {
-            if (err) {
-                dispatch(module.exports.setFetchStatus('shared', module.exports.Status.Status.ERROR));
-                dispatch(module.exports.setError(err));
-                return;
-            }
-            if (typeof body === 'undefined') {
-                dispatch(module.exports.setFetchStatus('shared', module.exports.Status.ERROR));
-                dispatch(module.exports.setError('No session content'));
-                return;
-            }
-            dispatch(module.exports.setFetchStatus('shared', module.exports.Status.FETCHED));
-            dispatch(module.exports.setRows('shared', body));
-        });
-    };
-};
+module.exports.getSharedByFollowing = (username, token) => (dispatch => {
+    dispatch(module.exports.setFetchStatus('shared', module.exports.Status.FETCHING));
+    api({
+        uri: `/users/${username}/following/users/projects`,
+        authentication: token
+    }, (err, body) => {
+        if (err) {
+            dispatch(module.exports.setFetchStatus('shared', module.exports.Status.Status.ERROR));
+            dispatch(module.exports.setError(err));
+            return;
+        }
+        if (typeof body === 'undefined') {
+            dispatch(module.exports.setFetchStatus('shared', module.exports.Status.ERROR));
+            dispatch(module.exports.setError('No session content'));
+            return;
+        }
+        dispatch(module.exports.setFetchStatus('shared', module.exports.Status.FETCHED));
+        dispatch(module.exports.setRows('shared', body));
+    });
+});
 
-/**
+/*
  * Get list of projects in studios that the given user is following.
  * @param  {string} username username of the Scratcher for whom to get projects
  * @param  {string} token    authentication
  */
-module.exports.getInStudiosFollowing = function (username, token) {
-    return function (dispatch) {
-        dispatch(module.exports.setFetchStatus('studios', module.exports.Status.FETCHING));
-        api({
-            uri: '/users/' + username + '/following/studios/projects',
-            authentication: token
-        }, function (err, body) {
-            if (err) {
-                dispatch(module.exports.setFetchStatus('studios', module.exports.Status.ERROR));
-                dispatch(module.exports.setError(err));
-                return;
-            }
-            if (typeof body === 'undefined') {
-                dispatch(module.exports.setFetchStatus('studios', module.exports.Status.ERROR));
-                dispatch(module.exports.setError('No session content'));
-                return;
-            }
-            dispatch(module.exports.setFetchStatus('studios', module.exports.Status.FETCHED));
-            dispatch(module.exports.setRows('studios', body));
-        });
-    };
-};
+module.exports.getInStudiosFollowing = (username, token) => (dispatch => {
+    dispatch(module.exports.setFetchStatus('studios', module.exports.Status.FETCHING));
+    api({
+        uri: `/users/${username}/following/studios/projects`,
+        authentication: token
+    }, (err, body) => {
+        if (err) {
+            dispatch(module.exports.setFetchStatus('studios', module.exports.Status.ERROR));
+            dispatch(module.exports.setError(err));
+            return;
+        }
+        if (typeof body === 'undefined') {
+            dispatch(module.exports.setFetchStatus('studios', module.exports.Status.ERROR));
+            dispatch(module.exports.setError('No session content'));
+            return;
+        }
+        dispatch(module.exports.setFetchStatus('studios', module.exports.Status.FETCHED));
+        dispatch(module.exports.setRows('studios', body));
+    });
+});
 
-/**
+/*
  * Get list of projects loved by users the given user is following.
  * @param  {string} username username of the Scratcher for whom to get projects
  * @param  {string} token    authentication
  */
-module.exports.getLovedByFollowing = function (username, token) {
-    return function (dispatch) {
-        dispatch(module.exports.setFetchStatus('loved', module.exports.Status.FETCHING));
-        api({
-            uri: '/users/' + username + '/following/users/loves',
-            authentication: token
-        }, function (err, body) {
-            if (err) {
-                dispatch(module.exports.setFetchStatus('loved', module.exports.Status.ERROR));
-                dispatch(module.exports.setError(err));
-                return;
-            }
-            if (typeof body === 'undefined') {
-                dispatch(module.exports.setFetchStatus('loved', module.exports.Status.ERROR));
-                dispatch(module.exports.setError('No session content'));
-                return;
-            }
-            dispatch(module.exports.setFetchStatus('loved', module.exports.Status.FETCHED));
-            dispatch(module.exports.setRows('loved', body));
-        });
-    };
-};
+module.exports.getLovedByFollowing = (username, token) => (dispatch => {
+    dispatch(module.exports.setFetchStatus('loved', module.exports.Status.FETCHING));
+    api({
+        uri: `/users/${username}/following/users/loves`,
+        authentication: token
+    }, (err, body) => {
+        if (err) {
+            dispatch(module.exports.setFetchStatus('loved', module.exports.Status.ERROR));
+            dispatch(module.exports.setError(err));
+            return;
+        }
+        if (typeof body === 'undefined') {
+            dispatch(module.exports.setFetchStatus('loved', module.exports.Status.ERROR));
+            dispatch(module.exports.setError('No session content'));
+            return;
+        }
+        dispatch(module.exports.setFetchStatus('loved', module.exports.Status.FETCHED));
+        dispatch(module.exports.setRows('loved', body));
+    });
+});
diff --git a/src/views/about/about.jsx b/src/views/about/about.jsx
index e2a6f73aa..2675bca69 100644
--- a/src/views/about/about.jsx
+++ b/src/views/about/about.jsx
@@ -1,177 +1,189 @@
-var React = require('react');
-var FormattedMessage = require('react-intl').FormattedMessage;
-var render = require('../../lib/render.jsx');
+const React = require('react');
+const FormattedMessage = require('react-intl').FormattedMessage;
+const render = require('../../lib/render.jsx');
 
-var Page = require('../../components/page/www/page.jsx');
+const Page = require('../../components/page/www/page.jsx');
 
 require('./about.scss');
 
-var About = React.createClass({
-    type: 'About',
-    render: function () {
-        return (
-            <div className="inner about">
-                <h1><FormattedMessage id='general.aboutScratch' /></h1>
+const About = () => (
+    <div className="inner about">
+        <h1><FormattedMessage id="general.aboutScratch" /></h1>
 
-                <div className="masthead">
-                    <div>
-                        <p><FormattedMessage id='about.introOne' /></p>
-                        <p><FormattedMessage id='about.introTwo' /></p>
-                        <p><FormattedMessage id='about.introThree' /></p>
+        <div className="masthead">
+            <div>
+                <p><FormattedMessage id="about.introOne" /></p>
+                <p><FormattedMessage id="about.introTwo" /></p>
+                <p><FormattedMessage id="about.introThree" /></p>
 
-                        <ul>
-                            <li><a href = "/parents/"><FormattedMessage id="about.introParents" /></a></li>
-                            <li><a href = "/educators/"><FormattedMessage id="about.introEducators" /></a></li>
-                        </ul>
-                    </div>
-
-                    <div>
-                        <iframe
-                            title="Scratch Overview Video"
-                            src="https://player.vimeo.com/video/65583694?title=0&byline=0&portrait=0"
-                            frameBorder="0"
-                            webkitAllowFullScreen
-                            mozallowfullscreen
-                            allowFullScreen />
-                    </div>
-                </div>
-
-                <div className="body">
-                    <ul>
-                        <li>
-                            <h3><FormattedMessage id='about.whoUsesScratch' /></h3>
-                            <img src="/images/about/who-uses-scratch.jpg" alt="" />
-                            <p><FormattedMessage id='about.whoUsesScratchDescription' /></p>
-                        </li>
-
-                        <li>
-                            <h3><FormattedMessage id='about.literacy' /></h3>
-                            <iframe
-                                src="https://embed-ssl.ted.com/talks/mitch_resnick_let_s_teach_kids_to_code.html"
-                                scrolling="no"
-                                webkitAllowFullScreen
-                                mozallowfullscreen
-                                allowFullScreen />
-                            <p><FormattedMessage id='about.literacyDescription' /></p>
-                        </li>
-
-                        <li>
-                            <h3><FormattedMessage id='about.aroundTheWorld' /></h3>
-                            <img src="/images/about/around-the-world.png" alt="" />
-                            <p><FormattedMessage
-                                id='about.aroundTheWorldDescription'
-                                values={{
-                                    translationLink: (
-                                        <a href='http://wiki.scratch.mit.edu/wiki/How_to_Translate_Scratch'>
-                                            <FormattedMessage id='about.translationLinkText' />
-                                        </a>
-                                    )
-                                }}
-                            /></p>
-                        </li>
-                        <li>
-                            <h3><FormattedMessage id='about.schools' /></h3>
-                            <img src="/images/about/scratch-in-schools.jpg" alt="" />
-                            <p><FormattedMessage
-                                id='about.schoolsDescription'
-                                values={{
-                                    scratchedLink: (
-                                        <a href='http://scratched.gse.harvard.edu/'>
-                                            <FormattedMessage id='about.scratchedLinkText' />
-                                        </a>
-                                    )
-                                }}
-                            /></p>
-                        </li>
-                        <li>
-                            <h3><FormattedMessage id='about.quotes' /></h3>
-                            <img src="/images/about/quotes.gif" alt="Quotes about Scratch" />
-                            <p><FormattedMessage
-                                id='about.quotesDescription'
-                                values={{
-                                    quotesLink: (
-                                        <a href='http://wiki.scratch.mit.edu/wiki/How_to_Translate_Scratch'>
-                                            <FormattedMessage id='about.quotesLinkText'/>
-                                        </a>
-                                    )
-                                }}
-                            /></p>
-                        </li>
-
-                        <li>
-                            <h3><FormattedMessage id='about.research' /></h3>
-                            <img src="/images/about/research-remix.png" alt="" />
-                            <p><FormattedMessage
-                                id='about.researchDescription'
-                                values={{
-                                    researchLink: (
-                                        <a href='/info/research'>
-                                            <FormattedMessage id='about.researchLinkText'/>
-                                        </a>
-                                    ),
-                                    spfaLink: (
-                                        <a href='http://web.media.mit.edu/~mres/papers/Scratch-CACM-final.pdf'>
-                                            <FormattedMessage id='about.spfaLinkText'/>
-                                        </a>
-                                    ),
-                                    statisticsLink: (
-                                        <a href='/statistics'>
-                                            <FormattedMessage id='about.statisticsLinkText'/>
-                                        </a>
-                                    )
-                                }}
-                            /></p>
-                        </li>
-
-                        <li>
-                            <h3><FormattedMessage id='about.learnMore' /></h3>
-                            <p>
-                                <ul className="list">
-                                    <li>
-                                        <a href="/help"><FormattedMessage id='about.learnMoreHelp' /></a>
-                                    </li>
-                                    <li>
-                                        <a href="/info/faq"><FormattedMessage id='about.learnMoreFaq' /></a>
-                                    </li>
-                                    <li>
-                                        <a href="/parents"><FormattedMessage id='about.learnMoreParents' /></a>
-                                    </li>
-                                    <li>
-                                        <a href="/info/credits"><FormattedMessage id='about.learnMoreCredits' /></a>
-                                    </li>
-                                </ul>
-                            </p>
-                        </li>
-
-                        <li>
-                            <h3><FormattedMessage id='about.support' /></h3>
-                            <p><FormattedMessage
-                                id='about.supportDescription'
-                                values={{
-                                    supportersList: 'National Science Foundation, Scratch Foundation, Siegel Family Endowment, Google, LEGO Foundation, Intel, Cartoon Network, Lemann Foundation, MacArthur Foundation', // eslint-disable-line max-len
-                                    creditsLink: (
-                                        <a href='//scratch.mit.edu/info/credits'>
-                                            <FormattedMessage id='about.creditsLinkText'/>
-                                        </a>
-                                    ),
-                                    donateLink: (
-                                        <a href='//secure.donationpay.org/scratchfoundation/'>
-                                            <FormattedMessage id='about.donateLinkText'/>
-                                        </a>
-                                    ),
-                                    donateemail: (
-                                        <a href='mailto:donate@scratch.mit.edu'>
-                                            donate@scratch.mit.edu
-                                        </a>
-                                    )
-                                }}
-                            /></p>
-                         </li>
-                    </ul>
-                </div>
+                <ul>
+                    <li><a href="/parents/"><FormattedMessage id="about.introParents" /></a></li>
+                    <li><a href="/educators/"><FormattedMessage id="about.introEducators" /></a></li>
+                </ul>
             </div>
-        );
-    }
-});
+
+            <div>
+                <iframe
+                    allowFullScreen
+                    mozallowfullscreen
+                    webkitAllowFullScreen
+                    frameBorder="0"
+                    src="https://player.vimeo.com/video/65583694?title=0&byline=0&portrait=0"
+                    title="Scratch Overview Video"
+                />
+            </div>
+        </div>
+
+        <div className="body">
+            <ul>
+                <li>
+                    <h3><FormattedMessage id="about.whoUsesScratch" /></h3>
+                    <img
+                        alt=""
+                        src="/images/about/who-uses-scratch.jpg"
+                    />
+                    <p><FormattedMessage id="about.whoUsesScratchDescription" /></p>
+                </li>
+
+                <li>
+                    <h3><FormattedMessage id="about.literacy" /></h3>
+                    <iframe
+                        allowFullScreen
+                        mozallowfullscreen
+                        webkitAllowFullScreen
+                        scrolling="no"
+                        src="https://embed-ssl.ted.com/talks/mitch_resnick_let_s_teach_kids_to_code.html"
+                    />
+                    <p><FormattedMessage id="about.literacyDescription" /></p>
+                </li>
+
+                <li>
+                    <h3><FormattedMessage id="about.aroundTheWorld" /></h3>
+                    <img
+                        alt=""
+                        src="/images/about/around-the-world.png"
+                    />
+                    <p><FormattedMessage
+                        id="about.aroundTheWorldDescription"
+                        values={{
+                            translationLink: (
+                                <a href="http://wiki.scratch.mit.edu/wiki/How_to_Translate_Scratch">
+                                    <FormattedMessage id="about.translationLinkText" />
+                                </a>
+                            )
+                        }}
+                    /></p>
+                </li>
+                <li>
+                    <h3><FormattedMessage id="about.schools" /></h3>
+                    <img
+                        alt=""
+                        src="/images/about/scratch-in-schools.jpg"
+                    />
+                    <p><FormattedMessage
+                        id="about.schoolsDescription"
+                        values={{
+                            scratchedLink: (
+                                <a href="http://scratched.gse.harvard.edu/">
+                                    <FormattedMessage id="about.scratchedLinkText" />
+                                </a>
+                            )
+                        }}
+                    /></p>
+                </li>
+                <li>
+                    <h3><FormattedMessage id="about.quotes" /></h3>
+                    <img
+                        alt="Quotes about Scratch"
+                        src="/images/about/quotes.gif"
+                    />
+                    <p><FormattedMessage
+                        id="about.quotesDescription"
+                        values={{
+                            quotesLink: (
+                                <a href="http://wiki.scratch.mit.edu/wiki/How_to_Translate_Scratch">
+                                    <FormattedMessage id="about.quotesLinkText" />
+                                </a>
+                            )
+                        }}
+                    /></p>
+                </li>
+
+                <li>
+                    <h3><FormattedMessage id="about.research" /></h3>
+                    <img
+                        alt=""
+                        src="/images/about/research-remix.png"
+                    />
+                    <p><FormattedMessage
+                        id="about.researchDescription"
+                        values={{
+                            researchLink: (
+                                <a href="/info/research">
+                                    <FormattedMessage id="about.researchLinkText" />
+                                </a>
+                            ),
+                            spfaLink: (
+                                <a href="http://web.media.mit.edu/~mres/papers/Scratch-CACM-final.pdf">
+                                    <FormattedMessage id="about.spfaLinkText" />
+                                </a>
+                            ),
+                            statisticsLink: (
+                                <a href="/statistics">
+                                    <FormattedMessage id="about.statisticsLinkText" />
+                                </a>
+                            )
+                        }}
+                    /></p>
+                </li>
+
+                <li>
+                    <h3><FormattedMessage id="about.learnMore" /></h3>
+                    <p>
+                        <ul className="list">
+                            <li>
+                                <a href="/help"><FormattedMessage id="about.learnMoreHelp" /></a>
+                            </li>
+                            <li>
+                                <a href="/info/faq"><FormattedMessage id="about.learnMoreFaq" /></a>
+                            </li>
+                            <li>
+                                <a href="/parents"><FormattedMessage id="about.learnMoreParents" /></a>
+                            </li>
+                            <li>
+                                <a href="/info/credits"><FormattedMessage id="about.learnMoreCredits" /></a>
+                            </li>
+                        </ul>
+                    </p>
+                </li>
+
+                <li>
+                    <h3><FormattedMessage id="about.support" /></h3>
+                    <p><FormattedMessage
+                        id="about.supportDescription"
+                        values={{
+                            supportersList: 'National Science Foundation, Scratch Foundation, Siegel Family Endowment, Google, LEGO Foundation, Intel, Cartoon Network, Lemann Foundation, MacArthur Foundation', // eslint-disable-line max-len
+                            creditsLink: (
+                                <a href="//scratch.mit.edu/info/credits">
+                                    <FormattedMessage id="about.creditsLinkText" />
+                                </a>
+                            ),
+                            donateLink: (
+                                <a href="//secure.donationpay.org/scratchfoundation/">
+                                    <FormattedMessage id="about.donateLinkText" />
+                                </a>
+                            ),
+                            donateemail: (
+                                <a href="mailto:donate@scratch.mit.edu">
+                                    donate@scratch.mit.edu
+                                </a>
+                            )
+                        }}
+                    /></p>
+                </li>
+            </ul>
+        </div>
+    </div>
+);
 
 render(<Page><About /></Page>, document.getElementById('app'));
diff --git a/src/views/camp/camp.jsx b/src/views/camp/camp.jsx
index e99edc6d3..b57ccf17d 100644
--- a/src/views/camp/camp.jsx
+++ b/src/views/camp/camp.jsx
@@ -1,138 +1,168 @@
-var React = require('react');
-var injectIntl = require('react-intl').injectIntl;
-var FormattedMessage = require('react-intl').FormattedMessage;
-var FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
-var render = require('../../lib/render.jsx');
-var TitleBanner = require('../../components/title-banner/title-banner.jsx');
-var FlexRow = require('../../components/flex-row/flex-row.jsx');
+const React = require('react');
+const injectIntl = require('react-intl').injectIntl;
+const FormattedMessage = require('react-intl').FormattedMessage;
+const FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
+const render = require('../../lib/render.jsx');
+const TitleBanner = require('../../components/title-banner/title-banner.jsx');
+const FlexRow = require('../../components/flex-row/flex-row.jsx');
 
-var Page = require('../../components/page/www/page.jsx');
+const Page = require('../../components/page/www/page.jsx');
 
 require('./camp.scss');
 
-var Camp = injectIntl(React.createClass({
-    type: 'Camp',
-    render: function () {
-        return (
-            <div>
-                <TitleBanner className="masthead mod-blue-bg">
-                    <img src="/images/camp/ocean-top.svg" alt="Top" className="topImg"/>
-                    <div className="inner">
-                        <div className="title-content">
-                            <h1 className="title-banner-h1">
-                                <FormattedMessage id='camp.title' />
-                            </h1>
-                            <h4 className="intro title-banner-p">
-                                <FormattedMessage id='camp.dates' />
-                            </h4>
-                            <img src="/images/camp/bubbles.svg" alt="Bubbles" className="bubbles"/>
-                        </div>
-                    </div>
-                </TitleBanner>
-                <div className="gradient1">
-                    <section id="particpate">
-                        <div className="inner">
-                            <center>
-                                <h2><FormattedMessage id='camp.welcome' /></h2>
-                                <p id="intro">
-                                    <FormattedHTMLMessage id='camp.welcomeIntro' />
-                                </p>
-                            </center>
-                            <center>
-                                <img src="/images/camp/fish-divider.svg" className="fishDivider" />
-                            </center>
-                            <h2>
-                                <center><FormattedMessage id='camp.part1Dates' /></center>
-                            </h2>
-                            <center>
-                            <FlexRow className="sidebar-row">
-                                <div className="body-copy column">
-                                    <h3><FormattedMessage id='camp.detailsTitle' /></h3>
-                                    <p>
-                                        <FormattedMessage id='camp.part1Details' />
-                                    </p>
-                                </div>
-                                <div className="sidebar column">
-                                    <h3><FormattedMessage id='camp.particpateTitle' /></h3>
-                                    <p>
-                                        <FormattedHTMLMessage id='camp.part1Particpate' />
-                                    </p>
-                                </div>
-                            </FlexRow>
-                            <center>
-                                <img src="/images/camp/fish-divider2.svg" className="fishDivider" />
-                            </center>
-                            <h2>
-                                <center><FormattedMessage id='camp.part2Dates' /></center>
-                            </h2>
-                            <FlexRow className="sidebar-row">
-                                <div className="body-copy column">
-                                    <h3><FormattedMessage id='camp.detailsTitle' /></h3>
-                                    <p>
-                                        <FormattedMessage id='camp.part2Details' />
-                                    </p>
-                                </div>
-                                <div className="sidebar column">
-                                    <h3><FormattedMessage id='camp.particpateTitle' /></h3>
-                                    <p>
-                                        <FormattedHTMLMessage id='camp.part2Particpate' />
-                                    </p>
-                                </div>
-                            </FlexRow>
-                            <center>
-                                <img src="/images/camp/fish-divider.svg" className="fishDivider" />
-                            </center>
-                            <h2>
-                                <center><FormattedMessage id='camp.part3Dates' /></center>
-                            </h2>
-                            <FlexRow className="sidebar-row">
-                                <div className="body-copy column">
-                                    <h3><FormattedMessage id='camp.detailsTitle' /></h3>
-                                    <p>
-                                        <FormattedMessage id='camp.part3Details' />
-                                    </p>
-                                </div>
-                                <div className="sidebar column">
-                                    <h3><FormattedMessage id='camp.particpateTitle' /></h3>
-                                    <p>
-                                        <FormattedHTMLMessage id='camp.part3Particpate' />
-                                    </p>
-                                </div>
-                            </FlexRow>
-                            </center>
-                        </div>
-                    </section>
-                    <img src="/images/camp/bubbles.svg" alt="Bubbles" className="bubbles"/>
-                    <section id="info">
-                        <div className="inner">
-                        <center><h2><FormattedMessage id='camp.helpfulInfo' /></h2></center>
-                            <FlexRow className="info-content">
-                                <div>
-                                    <img src="/images/camp/dolphin.svg" alt="Dolphin" className="infoImg" />
-                                    <p>
-                                        <FormattedHTMLMessage id='camp.infoCounselors' />
-                                    </p>
-                                </div>
-                                <div>
-                                    <img src="/images/camp/treasure.svg" className="infoImg" />
-                                    <p>
-                                        <FormattedMessage id='camp.infoPart3' />
-                                    </p>
-                                </div>
-                                <div>
-                                    <img src="/images/camp/map.svg" className="infoImg" />
-                                    <p>
-                                        <FormattedMessage id='camp.infoTime' />
-                                    </p>
-                                </div>
-                            </FlexRow>
-                        </div>
-                    </section>
-                    <img src="/images/camp/ocean-bottom.svg" alt="Top" className="bottomImg"/>
+const Camp = injectIntl(() => (
+    <div>
+        <TitleBanner className="masthead mod-blue-bg">
+            <img
+                alt="Top"
+                className="topImg"
+                src="/images/camp/ocean-top.svg"
+            />
+            <div className="inner">
+                <div className="title-content">
+                    <h1 className="title-banner-h1">
+                        <FormattedMessage id="camp.title" />
+                    </h1>
+                    <h4 className="intro title-banner-p">
+                        <FormattedMessage id="camp.dates" />
+                    </h4>
+                    <img
+                        alt="Bubbles"
+                        className="bubbles"
+                        src="/images/camp/bubbles.svg"
+                    />
                 </div>
             </div>
-        );
-    }
-}));
+        </TitleBanner>
+        <div className="gradient1">
+            <section id="particpate">
+                <div className="inner">
+                    <center>
+                        <h2><FormattedMessage id="camp.welcome" /></h2>
+                        <p id="intro">
+                            <FormattedHTMLMessage id="camp.welcomeIntro" />
+                        </p>
+                    </center>
+                    <center>
+                        <img
+                            className="fishDivider"
+                            src="/images/camp/fish-divider.svg"
+                        />
+                    </center>
+                    <h2>
+                        <center><FormattedMessage id="camp.part1Dates" /></center>
+                    </h2>
+                    <center>
+                        <FlexRow className="sidebar-row">
+                            <div className="body-copy column">
+                                <h3><FormattedMessage id="camp.detailsTitle" /></h3>
+                                <p>
+                                    <FormattedMessage id="camp.part1Details" />
+                                </p>
+                            </div>
+                            <div className="sidebar column">
+                                <h3><FormattedMessage id="camp.particpateTitle" /></h3>
+                                <p>
+                                    <FormattedHTMLMessage id="camp.part1Particpate" />
+                                </p>
+                            </div>
+                        </FlexRow>
+                        <center>
+                            <img
+                                className="fishDivider"
+                                src="/images/camp/fish-divider2.svg"
+                            />
+                        </center>
+                        <h2>
+                            <center><FormattedMessage id="camp.part2Dates" /></center>
+                        </h2>
+                        <FlexRow className="sidebar-row">
+                            <div className="body-copy column">
+                                <h3><FormattedMessage id="camp.detailsTitle" /></h3>
+                                <p>
+                                    <FormattedMessage id="camp.part2Details" />
+                                </p>
+                            </div>
+                            <div className="sidebar column">
+                                <h3><FormattedMessage id="camp.particpateTitle" /></h3>
+                                <p>
+                                    <FormattedHTMLMessage id="camp.part2Particpate" />
+                                </p>
+                            </div>
+                        </FlexRow>
+                        <center>
+                            <img
+                                className="fishDivider"
+                                src="/images/camp/fish-divider.svg"
+                            />
+                        </center>
+                        <h2>
+                            <center><FormattedMessage id="camp.part3Dates" /></center>
+                        </h2>
+                        <FlexRow className="sidebar-row">
+                            <div className="body-copy column">
+                                <h3><FormattedMessage id="camp.detailsTitle" /></h3>
+                                <p>
+                                    <FormattedMessage id="camp.part3Details" />
+                                </p>
+                            </div>
+                            <div className="sidebar column">
+                                <h3><FormattedMessage id="camp.particpateTitle" /></h3>
+                                <p>
+                                    <FormattedHTMLMessage id="camp.part3Particpate" />
+                                </p>
+                            </div>
+                        </FlexRow>
+                    </center>
+                </div>
+            </section>
+            <img
+                alt="Bubbles"
+                className="bubbles"
+                src="/images/camp/bubbles.svg"
+            />
+            <section id="info">
+                <div className="inner">
+                    <center><h2><FormattedMessage id="camp.helpfulInfo" /></h2></center>
+                    <FlexRow className="info-content">
+                        <div>
+                            <img
+                                alt="Dolphin"
+                                className="infoImg"
+                                src="/images/camp/dolphin.svg"
+                            />
+                            <p>
+                                <FormattedHTMLMessage id="camp.infoCounselors" />
+                            </p>
+                        </div>
+                        <div>
+                            <img
+                                className="infoImg"
+                                src="/images/camp/treasure.svg"
+                            />
+                            <p>
+                                <FormattedMessage id="camp.infoPart3" />
+                            </p>
+                        </div>
+                        <div>
+                            <img
+                                className="infoImg"
+                                src="/images/camp/map.svg"
+                            />
+                            <p>
+                                <FormattedMessage id="camp.infoTime" />
+                            </p>
+                        </div>
+                    </FlexRow>
+                </div>
+            </section>
+            <img
+                alt="Top"
+                className="bottomImg"
+                src="/images/camp/ocean-bottom.svg"
+            />
+        </div>
+    </div>
+));
 
 render(<Page><Camp /></Page>, document.getElementById('app'));
diff --git a/src/views/cards/cards.jsx b/src/views/cards/cards.jsx
index 76824d5c6..c18da2497 100644
--- a/src/views/cards/cards.jsx
+++ b/src/views/cards/cards.jsx
@@ -1,26 +1,33 @@
-var React = require('react');
-var injectIntl = require('react-intl').injectIntl;
-var FormattedMessage = require('react-intl').FormattedMessage;
-var render = require('../../lib/render.jsx');
+const bindAll = require('lodash.bindall');
+const injectIntl = require('react-intl').injectIntl;
+const intlShape = require('react-intl').intlShape;
+const FormattedMessage = require('react-intl').FormattedMessage;
+const React = require('react');
+const render = require('../../lib/render.jsx');
 
-var Box = require('../../components/box/box.jsx');
-var FlexRow = require('../../components/flex-row/flex-row.jsx');
-var Page = require('../../components/page/www/page.jsx');
+const Box = require('../../components/box/box.jsx');
+const FlexRow = require('../../components/flex-row/flex-row.jsx');
+const Page = require('../../components/page/www/page.jsx');
 
 require('./cards.scss');
 
-var Cards = injectIntl(React.createClass({
-    type: 'Cards',
-    pdfLocaleMismatch: function (locale, pdf, englishPdf) {
+class Cards extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll([
+            'pdfLocaleMismatch'
+        ]);
+    }
+    pdfLocaleMismatch (locale, pdf, englishPdf) {
         if (pdf === englishPdf && locale.indexOf('en') !== 0) {
             return true;
         }
         return false;
-    },
-    render: function () {
-        var locale = this.props.intl.locale || 'en';
-        var formatMessage = this.props.intl.formatMessage;
-        var englishLinks = {
+    }
+    render () {
+        const locale = this.props.intl.locale || 'en';
+        const formatMessage = this.props.intl.formatMessage;
+        const englishLinks = {
             'cards.starterLink': 'https://resources.scratch.mit.edu/www/cards/en/Scratch2Cards.pdf',
             'cards.nameLink': 'https://resources.scratch.mit.edu/www/cards/en/nameCards.pdf',
             'cards.flyLink': 'https://resources.scratch.mit.edu/www/cards/en/flyCards.pdf',
@@ -34,7 +41,7 @@ var Cards = injectIntl(React.createClass({
             'cards.catchLink': 'https://resources.scratch.mit.edu/www/cards/en/catchCards.pdf',
             'cards.petLink': 'https://resources.scratch.mit.edu/www/cards/en/petCards.pdf'
         };
-        var formattedLinks = {
+        const formattedLinks = {
             'cards.starterLink': formatMessage({id: 'cards.Scratch2CardsLink'}),
             'cards.nameLink': formatMessage({id: 'cards.nameCardsLink'}),
             'cards.flyLink': formatMessage({id: 'cards.flyCardsLink'}),
@@ -48,273 +55,411 @@ var Cards = injectIntl(React.createClass({
             'cards.catchLink': formatMessage({id: 'cards.catchCardsLink'}),
             'cards.petLink': formatMessage({id: 'cards.petCardsLink'})
         };
+
+        /* eslint-disable indent */
         return (
             <div className="inner cards">
                 <div className="cards-intro">
                     <div className="cards-intro-content">
                         <h1 className="cards-intro-content-header">
-                            <FormattedMessage id='cards.introHeader' />
+                            <FormattedMessage id="cards.introHeader" />
                         </h1>
                         <p className="cards-intro-content-body">
-                            <FormattedMessage id='cards.introContent' />
+                            <FormattedMessage id="cards.introContent" />
                         </p>
                     </div>
-                    <img src='/images/cards/card-use-overview.png'
-                         alt="Card Use Explanation"
-                         className="cards-intro-img" />
+                    <img
+                        alt="Card Use Explanation"
+                        className="cards-intro-img"
+                        src="/images/cards/card-use-overview.png"
+                    />
                 </div>
                 <div className="cards-container">
                     <Box title={''}>
                         <FlexRow>
                             <div className="flex-row-card">
                                 <h4 className="flex-row-card-header">
-                                    <FormattedMessage id='cards.starter' />
+                                    <FormattedMessage id="cards.starter" />
                                 </h4>
-                                <a className="flex-row-card-link" href={formattedLinks['cards.starterLink']}>
-                                    <img src="/images/cards/cards-starter.jpg" alt="" />
+                                <a
+                                    className="flex-row-card-link"
+                                    href={formattedLinks['cards.starterLink']}
+                                >
+                                    <img
+                                        alt=""
+                                        src="/images/cards/cards-starter.jpg"
+                                    />
                                 </a>
-                                <a className="flex-row-card-link" href={formattedLinks['cards.starterLink']}>
-                                    <img src="/svgs/pdf-icon-ui-blue.svg" alt="" className="flex-row-card-link-icon" />
-                                    <FormattedMessage id='cards.viewCard' />
-                                    {(
-                                        this.pdfLocaleMismatch(
-                                            locale,
-                                            formattedLinks['cards.starterLink'],
-                                            englishLinks['cards.starterLink']
-                                        )
-                                    ) ? [
-                                        <span> <FormattedMessage id='cards.english' /></span>
+                                <a
+                                    className="flex-row-card-link"
+                                    href={formattedLinks['cards.starterLink']}
+                                >
+                                    <img
+                                        alt=""
+                                        className="flex-row-card-link-icon"
+                                        src="/svgs/pdf-icon-ui-blue.svg"
+                                    />
+                                    <FormattedMessage id="cards.viewCard" />
+                                    {(this.pdfLocaleMismatch(
+                                        locale,
+                                        formattedLinks['cards.starterLink'],
+                                        englishLinks['cards.starterLink']
+                                    )) ? [
+                                        <span key="english-cards">
+                                            <FormattedMessage id="cards.english" />
+                                        </span>
                                     ] : []}
                                 </a>
                             </div>
                             <div className="flex-row-card">
                                 <h4 className="flex-row-card-header">
-                                    <FormattedMessage id='cards.name' />
+                                    <FormattedMessage id="cards.name" />
                                 </h4>
-                                <a className="flex-row-card-link" href={formattedLinks['cards.nameLink']}>
-                                    <img src="/images/cards/cards-name.jpg" alt="" />
+                                <a
+                                    className="flex-row-card-link"
+                                    href={formattedLinks['cards.nameLink']}
+                                >
+                                    <img
+                                        alt=""
+                                        src="/images/cards/cards-name.jpg"
+                                    />
                                 </a>
-                                <a className="flex-row-card-link" href={formattedLinks['cards.nameLink']}>
-                                    <img src="/svgs/pdf-icon-ui-blue.svg" alt="" className="flex-row-card-link-icon" />
-                                    <FormattedMessage id='cards.viewCard' />
-                                    {(
-                                        this.pdfLocaleMismatch(
-                                            locale,
-                                            formattedLinks['cards.nameLink'],
-                                            englishLinks['cards.nameLink']
-                                        )
-                                    ) ? [
-                                        <span> (<FormattedMessage id='cards.english' />)</span>
+                                <a
+                                    className="flex-row-card-link"
+                                    href={formattedLinks['cards.nameLink']}
+                                >
+                                    <img
+                                        alt=""
+                                        className="flex-row-card-link-icon"
+                                        src="/svgs/pdf-icon-ui-blue.svg"
+                                    />
+                                    <FormattedMessage id="cards.viewCard" />
+                                    {(this.pdfLocaleMismatch(
+                                        locale,
+                                        formattedLinks['cards.nameLink'],
+                                        englishLinks['cards.nameLink']
+                                    )) ? [
+                                        <span key="name-link"> (<FormattedMessage id="cards.english" />)</span>
                                     ] : []}
                                 </a>
                             </div>
                             <div className="flex-row-card">
                                 <h4 className="flex-row-card-header">
-                                    <FormattedMessage id='cards.fly' />
+                                    <FormattedMessage id="cards.fly" />
                                 </h4>
-                                <a className="flex-row-card-link" href={formattedLinks['cards.flyLink']}>
-                                    <img src="/images/cards/cards-fly.jpg" alt="" />
+                                <a
+                                    className="flex-row-card-link"
+                                    href={formattedLinks['cards.flyLink']}
+                                >
+                                    <img
+                                        alt=""
+                                        src="/images/cards/cards-fly.jpg"
+                                    />
                                 </a>
-                                <a className="flex-row-card-link" href={formattedLinks['cards.flyLink']}>
-                                    <img src="/svgs/pdf-icon-ui-blue.svg" alt="" className="flex-row-card-link-icon" />
-                                    <FormattedMessage id='cards.viewCard' />
-                                    {(
-                                        this.pdfLocaleMismatch(
-                                            locale,
-                                            formattedLinks['cards.flyLink'],
-                                            englishLinks['cards.flyLink']
-                                        )
-                                    ) ? [
-                                        <span> (<FormattedMessage id='cards.english' />)</span>
+                                <a
+                                    className="flex-row-card-link"
+                                    href={formattedLinks['cards.flyLink']}
+                                >
+                                    <img
+                                        alt=""
+                                        className="flex-row-card-link-icon"
+                                        src="/svgs/pdf-icon-ui-blue.svg"
+                                    />
+                                    <FormattedMessage id="cards.viewCard" />
+                                    {(this.pdfLocaleMismatch(
+                                        locale,
+                                        formattedLinks['cards.flyLink'],
+                                        englishLinks['cards.flyLink']
+                                    )) ? [
+                                        <span key="fly-link"> (<FormattedMessage id="cards.english" />)</span>
                                     ] : []}
                                 </a>
                             </div>
                             <div className="flex-row-card">
                                 <h4 className="flex-row-card-header">
-                                    <FormattedMessage id='cards.race' />
+                                    <FormattedMessage id="cards.race" />
                                 </h4>
-                                <a className="flex-row-card-link" href={formattedLinks['cards.raceLink']}>
-                                    <img src="/images/cards/cards-race.jpg" alt="" />
+                                <a
+                                    className="flex-row-card-link"
+                                    href={formattedLinks['cards.raceLink']}
+                                >
+                                    <img
+                                        alt=""
+                                        src="/images/cards/cards-race.jpg"
+                                    />
                                 </a>
-                                <a className="flex-row-card-link" href={formattedLinks['cards.raceLink']}>
-                                    <img src="/svgs/pdf-icon-ui-blue.svg" alt="" className="flex-row-card-link-icon" />
-                                    <FormattedMessage id='cards.viewCard' />
-                                    {(
-                                        this.pdfLocaleMismatch(
-                                            locale,
-                                            formattedLinks['cards.raceLink'],
-                                            englishLinks['cards.raceLink']
-                                        )
-                                    ) ? [
-                                        <span> (<FormattedMessage id='cards.english' />)</span>
+                                <a
+                                    className="flex-row-card-link"
+                                    href={formattedLinks['cards.raceLink']}
+                                >
+                                    <img
+                                        alt=""
+                                        className="flex-row-card-link-icon"
+                                        src="/svgs/pdf-icon-ui-blue.svg"
+                                    />
+                                    <FormattedMessage id="cards.viewCard" />
+                                    {(this.pdfLocaleMismatch(
+                                        locale,
+                                        formattedLinks['cards.raceLink'],
+                                        englishLinks['cards.raceLink']
+                                    )) ? [
+                                        <span key="race-link"> (<FormattedMessage id="cards.english" />)</span>
                                     ] : []}
                                 </a>
                             </div>
                             <div className="flex-row-card">
                                 <h4 className="flex-row-card-header">
-                                    <FormattedMessage id='cards.music' />
+                                    <FormattedMessage id="cards.music" />
                                 </h4>
-                                <a className="flex-row-card-link" href={formattedLinks['cards.musicLink']}>
-                                    <img src="/images/cards/cards-music.jpg" alt="" />
+                                <a
+                                    className="flex-row-card-link"
+                                    href={formattedLinks['cards.musicLink']}
+                                >
+                                    <img
+                                        alt=""
+                                        src="/images/cards/cards-music.jpg"
+                                    />
                                 </a>
-                                <a className="flex-row-card-link" href={formattedLinks['cards.musicLink']}>
-                                    <img src="/svgs/pdf-icon-ui-blue.svg" alt="" className="flex-row-card-link-icon" />
-                                    <FormattedMessage id='cards.viewCard' />
-                                    {(
-                                        this.pdfLocaleMismatch(
-                                            locale,
-                                            formattedLinks['cards.musicLink'],
-                                            englishLinks['cards.musicLink']
-                                        )
-                                    ) ? [
-                                        <span> (<FormattedMessage id='cards.english' />)</span>
+                                <a
+                                    className="flex-row-card-link"
+                                    href={formattedLinks['cards.musicLink']}
+                                >
+                                    <img
+                                        alt=""
+                                        className="flex-row-card-link-icon"
+                                        src="/svgs/pdf-icon-ui-blue.svg"
+                                    />
+                                    <FormattedMessage id="cards.viewCard" />
+                                    {(this.pdfLocaleMismatch(
+                                        locale,
+                                        formattedLinks['cards.musicLink'],
+                                        englishLinks['cards.musicLink']
+                                    )) ? [
+                                        <span key="music-link"> (<FormattedMessage id="cards.english" />)</span>
                                     ] : []}
                                 </a>
                             </div>
                             <div className="flex-row-card">
                                 <h4 className="flex-row-card-header">
-                                    <FormattedMessage id='cards.hide' />
+                                    <FormattedMessage id="cards.hide" />
                                 </h4>
-                                <a className="flex-row-card-link" href={formattedLinks['cards.hideLink']}>
-                                    <img src="/images/cards/cards-hide.jpg" alt="" />
+                                <a
+                                    className="flex-row-card-link"
+                                    href={formattedLinks['cards.hideLink']}
+                                >
+                                    <img
+                                        alt=""
+                                        src="/images/cards/cards-hide.jpg"
+                                    />
                                 </a>
-                                <a className="flex-row-card-link" href={formattedLinks['cards.hideLink']}>
-                                    <img src="/svgs/pdf-icon-ui-blue.svg" alt="" className="flex-row-card-link-icon" />
-                                    <FormattedMessage id='cards.viewCard' />
-                                    {(
-                                        this.pdfLocaleMismatch(
-                                            locale,
-                                            formattedLinks['cards.hideLink'],
-                                            englishLinks['cards.hideLink']
-                                        )
-                                    ) ? [
-                                        <span> (<FormattedMessage id='cards.english' />)</span>
+                                <a
+                                    className="flex-row-card-link"
+                                    href={formattedLinks['cards.hideLink']}
+                                >
+                                    <img
+                                        alt=""
+                                        className="flex-row-card-link-icon"
+                                        src="/svgs/pdf-icon-ui-blue.svg"
+                                    />
+                                    <FormattedMessage id="cards.viewCard" />
+                                    {(this.pdfLocaleMismatch(
+                                        locale,
+                                        formattedLinks['cards.hideLink'],
+                                        englishLinks['cards.hideLink']
+                                    )) ? [
+                                        <span key="hide-link"> (<FormattedMessage id="cards.english" />)</span>
                                     ] : []}
                                 </a>
                             </div>
                             <div className="flex-row-card">
                                 <h4 className="flex-row-card-header">
-                                    <FormattedMessage id='cards.story' />
+                                    <FormattedMessage id="cards.story" />
                                 </h4>
-                                <a className="flex-row-card-link" href={formattedLinks['cards.storyLink']}>
-                                    <img src="/images/cards/cards-story.jpg" alt="" />
+                                <a
+                                    className="flex-row-card-link"
+                                    href={formattedLinks['cards.storyLink']}
+                                >
+                                    <img
+                                        alt=""
+                                        src="/images/cards/cards-story.jpg"
+                                    />
                                 </a>
-                                <a className="flex-row-card-link" href={formattedLinks['cards.storyLink']}>
-                                    <img src="/svgs/pdf-icon-ui-blue.svg" alt="" className="flex-row-card-link-icon" />
-                                    <FormattedMessage id='cards.viewCard' />
-                                    {(
-                                        this.pdfLocaleMismatch(
-                                            locale,
-                                            formattedLinks['cards.storyLink'],
-                                            englishLinks['cards.storyLink']
-                                        )
-                                    ) ? [
-                                        <span> (<FormattedMessage id='cards.english' />)</span>
+                                <a
+                                    className="flex-row-card-link"
+                                    href={formattedLinks['cards.storyLink']}
+                                >
+                                    <img
+                                        alt=""
+                                        className="flex-row-card-link-icon"
+                                        src="/svgs/pdf-icon-ui-blue.svg"
+                                    />
+                                    <FormattedMessage id="cards.viewCard" />
+                                    {(this.pdfLocaleMismatch(
+                                        locale,
+                                        formattedLinks['cards.storyLink'],
+                                        englishLinks['cards.storyLink']
+                                    )) ? [
+                                        <span key="story-link"> (<FormattedMessage id="cards.english" />)</span>
                                     ] : []}
                                 </a>
                             </div>
                             <div className="flex-row-card">
                                 <h4 className="flex-row-card-header">
-                                    <FormattedMessage id='cards.dressup' />
+                                    <FormattedMessage id="cards.dressup" />
                                 </h4>
-                                <a className="flex-row-card-link" href={formattedLinks['cards.dressupLink']}>
-                                    <img src="/images/cards/cards-dressup.jpg" alt="" />
+                                <a
+                                    className="flex-row-card-link"
+                                    href={formattedLinks['cards.dressupLink']}
+                                >
+                                    <img
+                                        alt=""
+                                        src="/images/cards/cards-dressup.jpg"
+                                    />
                                 </a>
-                                <a className="flex-row-card-link" href={formattedLinks['cards.dressupLink']}>
-                                    <img src="/svgs/pdf-icon-ui-blue.svg" alt="" className="flex-row-card-link-icon" />
-                                    <FormattedMessage id='cards.viewCard' />
-                                    {(
-                                        this.pdfLocaleMismatch(
-                                            locale,
-                                            formattedLinks['cards.dressupLink'],
-                                            englishLinks['cards.dressupLink']
-                                        )
-                                    ) ? [
-                                        <span> (<FormattedMessage id='cards.english' />)</span>
+                                <a
+                                    className="flex-row-card-link"
+                                    href={formattedLinks['cards.dressupLink']}
+                                >
+                                    <img
+                                        alt=""
+                                        className="flex-row-card-link-icon"
+                                        src="/svgs/pdf-icon-ui-blue.svg"
+                                    />
+                                    <FormattedMessage id="cards.viewCard" />
+                                    {(this.pdfLocaleMismatch(
+                                        locale,
+                                        formattedLinks['cards.dressupLink'],
+                                        englishLinks['cards.dressupLink']
+                                    )) ? [
+                                        <span key="dress-link"> (<FormattedMessage id="cards.english" />)</span>
                                     ] : []}
                                 </a>
                             </div>
                             <div className="flex-row-card">
                                 <h4 className="flex-row-card-header">
-                                    <FormattedMessage id='cards.pong' />
+                                    <FormattedMessage id="cards.pong" />
                                 </h4>
-                                <a className="flex-row-card-link" href={formattedLinks['cards.pongLink']}>
-                                    <img src="/images/cards/cards-pong.jpg" alt="" />
+                                <a
+                                    className="flex-row-card-link"
+                                    href={formattedLinks['cards.pongLink']}
+                                >
+                                    <img
+                                        alt=""
+                                        src="/images/cards/cards-pong.jpg"
+                                    />
                                 </a>
-                                <a className="flex-row-card-link" href={formattedLinks['cards.pongLink']}>
-                                    <img src="/svgs/pdf-icon-ui-blue.svg" alt="" className="flex-row-card-link-icon" />
-                                    <FormattedMessage id='cards.viewCard' />
-                                    {(
-                                        this.pdfLocaleMismatch(
-                                            locale,
-                                            formattedLinks['cards.pongLink'],
-                                            englishLinks['cards.pongLink']
-                                        )
-                                    ) ? [
-                                        <span> (<FormattedMessage id='cards.english' />)</span>
+                                <a
+                                    className="flex-row-card-link"
+                                    href={formattedLinks['cards.pongLink']}
+                                >
+                                    <img
+                                        alt=""
+                                        className="flex-row-card-link-icon"
+                                        src="/svgs/pdf-icon-ui-blue.svg"
+                                    />
+                                    <FormattedMessage id="cards.viewCard" />
+                                    {(this.pdfLocaleMismatch(
+                                        locale,
+                                        formattedLinks['cards.pongLink'],
+                                        englishLinks['cards.pongLink']
+                                    )) ? [
+                                        <span key="pong-link"> (<FormattedMessage id="cards.english" />)</span>
                                     ] : []}
                                 </a>
                             </div>
                             <div className="flex-row-card">
                                 <h4 className="flex-row-card-header">
-                                    <FormattedMessage id='cards.dance' />
+                                    <FormattedMessage id="cards.dance" />
                                 </h4>
-                                <a className="flex-row-card-link" href={formattedLinks['cards.danceLink']}>
-                                    <img src="/images/cards/cards-dance.jpg" alt="" />
+                                <a
+                                    className="flex-row-card-link"
+                                    href={formattedLinks['cards.danceLink']}
+                                >
+                                    <img
+                                        alt=""
+                                        src="/images/cards/cards-dance.jpg"
+                                    />
                                 </a>
-                                <a className="flex-row-card-link" href={formattedLinks['cards.danceLink']}>
-                                    <img src="/svgs/pdf-icon-ui-blue.svg" alt="" className="flex-row-card-link-icon" />
-                                    <FormattedMessage id='cards.viewCard' />
-                                    {(
-                                        this.pdfLocaleMismatch(
-                                            locale,
-                                            formattedLinks['cards.danceLink'],
-                                            englishLinks['cards.danceLink']
-                                        )
-                                    ) ? [
-                                        <span> (<FormattedMessage id='cards.english' />)</span>
+                                <a
+                                    className="flex-row-card-link"
+                                    href={formattedLinks['cards.danceLink']}
+                                >
+                                    <img
+                                        alt=""
+                                        className="flex-row-card-link-icon"
+                                        src="/svgs/pdf-icon-ui-blue.svg"
+                                    />
+                                    <FormattedMessage id="cards.viewCard" />
+                                    {(this.pdfLocaleMismatch(
+                                        locale,
+                                        formattedLinks['cards.danceLink'],
+                                        englishLinks['cards.danceLink']
+                                    )) ? [
+                                        <span key="dance-link"> (<FormattedMessage id="cards.english" />)</span>
                                     ] : []}
                                 </a>
                             </div>
                             <div className="flex-row-card">
                                 <h4 className="flex-row-card-header">
-                                    <FormattedMessage id='cards.catch' />
+                                    <FormattedMessage id="cards.catch" />
                                 </h4>
-                                <a className="flex-row-card-link" href={formattedLinks['cards.catchLink']}>
-                                    <img src="/images/cards/cards-catch.jpg" alt="" />
+                                <a
+                                    className="flex-row-card-link"
+                                    href={formattedLinks['cards.catchLink']}
+                                >
+                                    <img
+                                        alt=""
+                                        src="/images/cards/cards-catch.jpg"
+                                    />
                                 </a>
-                                <a className="flex-row-card-link" href={formattedLinks['cards.catchLink']}>
-                                    <img src="/svgs/pdf-icon-ui-blue.svg" alt="" className="flex-row-card-link-icon" />
-                                    <FormattedMessage id='cards.viewCard' />
-                                    {(
-                                        this.pdfLocaleMismatch(
-                                            locale,
-                                            formattedLinks['cards.catchLink'],
-                                            englishLinks['cards.catchLink']
-                                        )
-                                    ) ? [
-                                        <span> (<FormattedMessage id='cards.english' />)</span>
+                                <a
+                                    className="flex-row-card-link"
+                                    href={formattedLinks['cards.catchLink']}
+                                >
+                                    <img
+                                        alt=""
+                                        className="flex-row-card-link-icon"
+                                        src="/svgs/pdf-icon-ui-blue.svg"
+                                    />
+                                    <FormattedMessage id="cards.viewCard" />
+                                    {(this.pdfLocaleMismatch(
+                                        locale,
+                                        formattedLinks['cards.catchLink'],
+                                        englishLinks['cards.catchLink']
+                                    )) ? [
+                                        <span key="catch-link"> (<FormattedMessage id="cards.english" />)</span>
                                     ] : []}
                                 </a>
                             </div>
                             <div className="flex-row-card">
                                 <h4 className="flex-row-card-header">
-                                    <FormattedMessage id='cards.pet' />
+                                    <FormattedMessage id="cards.pet" />
                                 </h4>
-                                <a className="flex-row-card-link" href={formattedLinks['cards.petLink']}>
-                                    <img src="/images/cards/cards-pet.jpg" alt="" />
+                                <a
+                                    className="flex-row-card-link"
+                                    href={formattedLinks['cards.petLink']}
+                                >
+                                    <img
+                                        alt=""
+                                        src="/images/cards/cards-pet.jpg"
+                                    />
                                 </a>
-                                <a className="flex-row-card-link" href={formattedLinks['cards.petLink']}>
-                                    <img src="/svgs/pdf-icon-ui-blue.svg" alt="" className="flex-row-card-link-icon" />
-                                    <FormattedMessage id='cards.viewCard' />
-                                    {(
-                                        this.pdfLocaleMismatch(
-                                            locale,
-                                            formattedLinks['cards.petLink'],
-                                            englishLinks['cards.petLink']
-                                        )
-                                    ) ? [
-                                        <span> (<FormattedMessage id='cards.english' />)</span>
+                                <a
+                                    className="flex-row-card-link"
+                                    href={formattedLinks['cards.petLink']}
+                                >
+                                    <img
+                                        alt=""
+                                        className="flex-row-card-link-icon"
+                                        src="/svgs/pdf-icon-ui-blue.svg"
+                                    />
+                                    <FormattedMessage id="cards.viewCard" />
+                                    {(this.pdfLocaleMismatch(
+                                        locale,
+                                        formattedLinks['cards.petLink'],
+                                        englishLinks['cards.petLink']
+                                    )) ? [
+                                        <span key="pet-link"> (<FormattedMessage id="cards.english" />)</span>
                                     ] : []}
                                 </a>
                             </div>
@@ -324,6 +469,12 @@ var Cards = injectIntl(React.createClass({
             </div>
         );
     }
-}));
+}
 
-render(<Page><Cards /></Page>, document.getElementById('app'));
+Cards.propTypes = {
+    intl: intlShape
+};
+
+const LocalizedCards = injectIntl(Cards);
+
+render(<Page><LocalizedCards /></Page>, document.getElementById('app'));
diff --git a/src/views/communityblocks-interviews/communityblocks-interviews.jsx b/src/views/communityblocks-interviews/communityblocks-interviews.jsx
index 89d1d7243..91d86dc71 100644
--- a/src/views/communityblocks-interviews/communityblocks-interviews.jsx
+++ b/src/views/communityblocks-interviews/communityblocks-interviews.jsx
@@ -1,62 +1,86 @@
-var React = require('react');
-var render = require('../../lib/render.jsx');
+const React = require('react');
+const render = require('../../lib/render.jsx');
 
-var Page = require('../../components/page/www/page.jsx');
+const Page = require('../../components/page/www/page.jsx');
 
-var CommunityBlocksInterviews = React.createClass({
-    type: 'communityblocks-interviews',
-    render: function () {
-        return (
-        <div className="inner">
-          <h2>Community Blocks Beta Tester Interviews</h2>
-          <br/>
-          <p>
+const CommunityBlocksInterviews = () => (
+    <div className="inner">
+        <h2>Community Blocks Beta Tester Interviews</h2>
+        <br />
+        <p>
             Hello Scratchers!
-          </p>
-          <p>
-            I am Sayamindu Dasgupta (<a href="/users/sdg1/" target="_blank">sdg1</a> on Scratch) and I am a member of
-            the <a href="/info/credits" target="_blank">MIT Scratch Team</a>.
-          </p>
-          <p>
+        </p>
+        <p>
+            I am Sayamindu Dasgupta (<a
+                href="/users/sdg1/"
+                rel="noopener noreferrer"
+                target="_blank"
+            >
+                sdg1
+            </a> on Scratch) and I am a member of
+            the <a
+                href="/info/credits"
+                rel="noopener noreferrer"
+                target="_blank"
+            >
+                MIT Scratch Team
+            </a>.
+        </p>
+        <p>
             One of our projects on the MIT Scratch Team is to understand how people use Scratch, the Scratch Community
             Blocks, and participate in the Scratch community. To do this, we are talking to Scratchers who have been
             particapting the Community Blocks beta testing program directly through interviews. In the interview, we
             would talk for an hour, asking about your Scratch experience (by phone or a service like Skype).
-          </p>
-          <p>
+        </p>
+        <p>
             Thank you for indicating in the beta invitation survey that you are willing to be interviewed.
             If you are still interested, please do the following steps:
-          </p>
+        </p>
 
-          <ul>
+        <ul>
+            <li>
+                <b>Complete the consent forms:</b> If you are under 18 years old, please download and complete these
+                two forms (<a href="/pdfs/interviews/communityblocks/assent_form.pdf">one for you to sign</a>&nbsp;
+                and <a href="/pdfs/interviews/communityblocks/consent_for_parent.pdf">
+                    another for your parent to sign
+                </a>).
+                If you are 18 years old and over, please complete&nbsp;
+                <a href="/pdfs/interviews/communityblocks/consent_for_over_18.pdf">this form</a>.
+            </li>
+            <li>
+                <b>Send the forms:</b> You can send me the forms in two ways: (1) by email
+                (<a
+                    href="mailto:sayamindu@media.mit.edu"
+                    rel="noopener noreferrer"
+                    target="_blank"
+                >
+                    sayamindu@media.mit.edu
+                </a>) by taking a picture
+                or scanning the forms, or (2) send it through snail mail to Sayamindu Dasgupta, 77 Massachusetts Ave
+                E14-464A, Cambridge, MA 02139
+            </li>
+            <li>
+                <b>Schedule a time to talk:</b> Send me an email
+                (<a
+                    href="mailto:sayamindu@media.mit.edu"
+                    rel="noopener noreferrer"
+                    target="_blank"
+                >
+                    sayamindu@media.mit.edu
+                </a>) with a possible time
+                where we can talk for about an hour.
+            </li>
+            <li>
+                If you have any questions, please do not hesitate to contact me by sending me an email
+                at <a href="mailto:sayamindu@media.mit.edu">sayamindu@media.mit.edu</a>.
+            </li>
+        </ul>
 
-            <li><b>Complete the consent forms:</b> If you are under 18 years old, please download and complete these
-            two forms (<a href="/pdfs/interviews/communityblocks/assent_form.pdf">one for you to sign</a>&nbsp;
-            and <a href="/pdfs/interviews/communityblocks/consent_for_parent.pdf">another for your parent to sign</a>).
-            If you are 18 years old and over, please complete&nbsp;
-            <a href="/pdfs/interviews/communityblocks/consent_for_over_18.pdf">this form</a>.</li>
-
-            <li><b>Send the forms:</b> You can send me the forms in two ways: (1) by email
-            (<a href="mailto:sayamindu@media.mit.edu" target="_blank">sayamindu@media.mit.edu</a>) by taking a picture
-            or scanning the forms, or (2) send it through snail mail to Sayamindu Dasgupta, 77 Massachusetts Ave
-            E14-464A, Cambridge, MA 02139</li>
-
-            <li><b>Schedule a time to talk:</b> Send me an email
-            (<a href="mailto:sayamindu@media.mit.edu" target="_blank">sayamindu@media.mit.edu</a>) with a possible time
-            where we can talk for about an hour.</li>
-
-            <li>If you have any questions, please do not hesitate to contact me by sending me an email
-            at <a href="mailto:sayamindu@media.mit.edu">sayamindu@media.mit.edu</a>.</li>
-
-          </ul>
-
-          <p>
+        <p>
             Thank you and I look forward to speaking with you. Scratch on!
-          </p>
-          Sayamindu
-        </div>
-        );
-    }
-});
+        </p>
+        Sayamindu
+    </div>
+);
 
 render(<Page><CommunityBlocksInterviews /></Page>, document.getElementById('app'));
diff --git a/src/views/components/components.jsx b/src/views/components/components.jsx
index 259485334..5f481d019 100644
--- a/src/views/components/components.jsx
+++ b/src/views/components/components.jsx
@@ -1,67 +1,68 @@
-var React = require('react');
-var render = require('../../lib/render.jsx');
+const React = require('react');
+const render = require('../../lib/render.jsx');
 
-var Page = require('../../components/page/www/page.jsx');
-var Box = require('../../components/box/box.jsx');
-var Button = require('../../components/forms/button.jsx');
-var Carousel = require('../../components/carousel/carousel.jsx');
-var Input = require('../../components/forms/input.jsx');
-var Spinner = require('../../components/spinner/spinner.jsx');
+const Page = require('../../components/page/www/page.jsx');
+const Box = require('../../components/box/box.jsx');
+const Button = require('../../components/forms/button.jsx');
+const Carousel = require('../../components/carousel/carousel.jsx');
+const Input = require('../../components/forms/input.jsx');
+const Spinner = require('../../components/spinner/spinner.jsx');
 
 require('./components.scss');
 
-var Components = React.createClass({
-    type: 'Components',
-    render: function () {
-        return (<div className="components">
-            <div className="inner">
-                <h1>Button</h1>
-                <Button>I love button</Button>
-                <h1>Form</h1>
-                <Input type="text" name="test" maxLength="30" />
-                <h1>Box Component</h1>
-                <Box
-                    title="Some Title"
-                    more="Cat Gifs"
-                    moreUrl="http://www.catgifpage.com/">
-                    <h4>Things go in here</h4>
-                    <p>Lorem ipsum dolor sit amet.</p>
-                </Box>
-                <h1>Carousel Component</h1>
+const Components = () => (
+    <div className="components">
+        <div className="inner">
+            <h1>Button</h1>
+            <Button>I love button</Button>
+            <h1>Form</h1>
+            <Input
+                maxLength="30"
+                name="test"
+                type="text"
+            />
+            <h1>Box Component</h1>
+            <Box
+                more="Cat Gifs"
+                moreUrl="http://www.catgifpage.com/"
+                title="Some Title"
+            >
+                <h4>Things go in here</h4>
+                <p>Lorem ipsum dolor sit amet.</p>
+            </Box>
+            <h1>Carousel Component</h1>
+            <Carousel />
+            <Box title="Carousel component in a box!">
                 <Carousel />
-                <Box
-                    title="Carousel component in a box!">
-                    <Carousel />
-                </Box>
-                <h1>This is a Spinner</h1>
-                <Spinner />
-                <h1>Colors</h1>
-                <div className="colors">
-                    <span className="ui-blue">$ui-blue</span>
-                    <span className="ui-orange">$ui-orange</span>
-                    <span className="ui-light-gray">$ui-light-gray</span>
-                    <span className="ui-gray">$ui-gray</span>
-                    <span className="ui-dark-gray">$ui-dark-gray</span>
-                    <span className="background-color">$background-color</span>
-                    <span className="ui-aqua">$ui-aqua</span>
-                    <span className="ui-purple">$ui-purple</span>
-                    <span className="ui-white">$ui-white</span>
-                    <span className="ui-border">$ui-border</span>
-                    <span className="active-gray">$active-gray</span>
-                    <span className="active-dark-gray">$active-dark-gray</span>
-                    <span className="box-shadow-gray">$box-shadow-gray</span>
-                    <span className="overlay-gray">$overlay-gray</span>
-                    <span className="header-gray">$header-gray</span>
-                    <span className="type-gray">$type-gray</span>
-                    <span className="type-white">$type-white</span>
-                    <span className="link-blue">$link-blue</span>
-                    <span className="splash-green">$splash-green</span>
-                    <span className="splash-pink">$splash-pink</span>
-                    <span className="splash-blue">$splash-blue</span>
-                </div>
+            </Box>
+            <h1>This is a Spinner</h1>
+            <Spinner />
+            <h1>Colors</h1>
+            <div className="colors">
+                <span className="ui-blue">$ui-blue</span>
+                <span className="ui-orange">$ui-orange</span>
+                <span className="ui-light-gray">$ui-light-gray</span>
+                <span className="ui-gray">$ui-gray</span>
+                <span className="ui-dark-gray">$ui-dark-gray</span>
+                <span className="background-color">$background-color</span>
+                <span className="ui-aqua">$ui-aqua</span>
+                <span className="ui-purple">$ui-purple</span>
+                <span className="ui-white">$ui-white</span>
+                <span className="ui-border">$ui-border</span>
+                <span className="active-gray">$active-gray</span>
+                <span className="active-dark-gray">$active-dark-gray</span>
+                <span className="box-shadow-gray">$box-shadow-gray</span>
+                <span className="overlay-gray">$overlay-gray</span>
+                <span className="header-gray">$header-gray</span>
+                <span className="type-gray">$type-gray</span>
+                <span className="type-white">$type-white</span>
+                <span className="link-blue">$link-blue</span>
+                <span className="splash-green">$splash-green</span>
+                <span className="splash-pink">$splash-pink</span>
+                <span className="splash-blue">$splash-blue</span>
             </div>
-        </div>);
-    }
-});
+        </div>
+    </div>
+);
 
 render(<Page><Components /></Page>, document.getElementById('app'));
diff --git a/src/views/conference/2016/details/details.jsx b/src/views/conference/2016/details/details.jsx
index 3dfbf3871..19d98603a 100644
--- a/src/views/conference/2016/details/details.jsx
+++ b/src/views/conference/2016/details/details.jsx
@@ -1,38 +1,34 @@
-var classNames = require('classnames');
-var connect = require('react-redux').connect;
-var React = require('react');
-var render = require('../../../../lib/render.jsx');
+const classNames = require('classnames');
+const connect = require('react-redux').connect;
+const PropTypes = require('prop-types');
+const React = require('react');
+const render = require('../../../../lib/render.jsx');
 
-var detailsActions = require('../../../../redux/conference-details.js');
+const detailsActions = require('../../../../redux/conference-details.js');
 
-var Page = require('../../../../components/page/conference/2016/page.jsx');
+const Page = require('../../../../components/page/conference/2016/page.jsx');
 
 require('./details.scss');
 
-var ConferenceDetails = React.createClass({
-    type: 'ConferenceDetails',
-    propTypes: {
-        detailsId: React.PropTypes.number,
-        conferenceDetails: React.PropTypes.object
-    },
-    componentDidMount: function () {
-        var pathname = window.location.pathname.toLowerCase();
+class ConferenceDetails extends React.Component {
+    componentDidMount () {
+        let pathname = window.location.pathname.toLowerCase();
         if (pathname[pathname.length - 1] === '/') {
             pathname = pathname.substring(0, pathname.length - 1);
         }
-        var path = pathname.split('/');
-        var detailsId = path[path.length - 2];
+        const path = pathname.split('/');
+        const detailsId = path[path.length - 2];
         this.props.dispatch(detailsActions.startGetDetails(detailsId));
-    },
-    render: function () {
-        var backUri = '/conference/2016/schedule';
+    }
+    render () {
+        let backUri = '/conference/2016/schedule';
         if (!this.props.conferenceDetails.error && !this.props.conferenceDetails.fetching) {
-            backUri = backUri + '#' + this.props.conferenceDetails.Day;
+            backUri = `${backUri}#${this.props.conferenceDetails.Day}`;
         }
-        var classes = classNames({
-            'inner': true,
-            'details': true,
-            'fetching': this.props.conferenceDetails.fetching
+        const classes = classNames({
+            inner: true,
+            details: true,
+            fetching: this.props.conferenceDetails.fetching
         });
         return (
             <div className={classes}>
@@ -42,54 +38,81 @@ var ConferenceDetails = React.createClass({
                     </a>
                 </div>
                 {this.props.conferenceDetails.error ? [
-                    <h2>Agenda Item Not Found</h2>
+                    <h2 key="not-found">Agenda Item Not Found</h2>
                 ] : [
-                    <h2>{this.props.conferenceDetails.Title}</h2>,
-                    <ul className="logistics">
+                    <h2 key="details-title">{this.props.conferenceDetails.Title}</h2>,
+                    <ul
+                        className="logistics"
+                        key="details-logistics"
+                    >
                         {this.props.conferenceDetails.fetching ? [] : [
-                            <li>
-                                <img src="/svgs/conference/schedule/presenter-icon.svg" alt="presenter icon" />
+                            <li key="presenter">
+                                <img
+                                    alt="presenter icon"
+                                    src="/svgs/conference/schedule/presenter-icon.svg"
+                                />
                                 {this.props.conferenceDetails.Presenter}
                             </li>,
-                            <li>
-                                <img src="/svgs/conference/schedule/time-icon.svg" alt="time icon" />
+                            <li key="start">
+                                <img
+                                    alt="time icon"
+                                    src="/svgs/conference/schedule/time-icon.svg"
+                                />
                                 {this.props.conferenceDetails.Start} &ndash; {this.props.conferenceDetails.End}
                             </li>,
-                            <li>
-                                <img src="/svgs/conference/schedule/event-icon.svg" alt="event icon" />
+                            <li key="type">
+                                <img
+                                    alt="event icon"
+                                    src="/svgs/conference/schedule/event-icon.svg"
+                                />
                                 {this.props.conferenceDetails.Type}
                             </li>,
-                            <li>
-                                <img src="/svgs/conference/schedule/location-icon.svg" alt="location icon" />
+                            <li key="location">
+                                <img
+                                    alt="location icon"
+                                    src="/svgs/conference/schedule/location-icon.svg"
+                                />
                                 {this.props.conferenceDetails.Location}
                             </li>
                         ]}
                     </ul>,
-                    <div className="description">
+                    <div
+                        className="description"
+                        key="details-desc"
+                    >
                         <p>
                             {this.props.conferenceDetails.Description}
                         </p>
                     </div>,
-                    <div className="back">
-                    {this.props.conferenceDetails.fetching ? [] : [
-                        <a href={backUri}>
-                            &larr; Back to Full Schedule
-                        </a>
-                    ]}
+                    <div
+                        className="back"
+                        key="details-back"
+                    >
+                        {this.props.conferenceDetails.fetching ? [] : [
+                            <a
+                                href={backUri}
+                                key="details-back-uri"
+                            >
+                                &larr; Back to Full Schedule
+                            </a>
+                        ]}
                     </div>
                 ]}
             </div>
         );
     }
-});
+}
 
-var mapStateToProps = function (state) {
-    return {
-        conferenceDetails: state.conferenceDetails
-    };
+ConferenceDetails.propTypes = {
+    conferenceDetails: PropTypes.object, // eslint-disable-line react/forbid-prop-types
+    dispatch: PropTypes.func
 };
 
-var ConnectedDetails = connect(mapStateToProps)(ConferenceDetails);
+const mapStateToProps = state => ({
+    conferenceDetails: state.conferenceDetails
+});
+
+const ConnectedDetails = connect(mapStateToProps)(ConferenceDetails);
 
 render(
     <Page><ConnectedDetails /></Page>,
diff --git a/src/views/conference/2016/expect/expect.jsx b/src/views/conference/2016/expect/expect.jsx
index 4d4468cd7..08ed20218 100644
--- a/src/views/conference/2016/expect/expect.jsx
+++ b/src/views/conference/2016/expect/expect.jsx
@@ -1,286 +1,306 @@
-var React = require('react');
-var render = require('../../../../lib/render.jsx');
+const React = require('react');
+const render = require('../../../../lib/render.jsx');
 
-var FlexRow = require('../../../../components/flex-row/flex-row.jsx');
-var Page = require('../../../../components/page/conference/2016/page.jsx');
-var TitleBanner = require('../../../../components/title-banner/title-banner.jsx');
+const FlexRow = require('../../../../components/flex-row/flex-row.jsx');
+const Page = require('../../../../components/page/conference/2016/page.jsx');
+const TitleBanner = require('../../../../components/title-banner/title-banner.jsx');
 
 require('./expect.scss');
 
-var ConferenceExpectations = React.createClass({
-    render: function () {
-        return (
-            <div className="expect">
-                <TitleBanner className="mod-conference">
-                    <h1>
-                        What to Expect
-                    </h1>
-                    <div className="title-icon">
-                        <img src="/images/conference/expect/what-to-expect.png" alt="expect-image" />
-                    </div>
-                </TitleBanner>
-                <section className="inner profile">
-                    <FlexRow className="uneven">
-                        <div className="short">
-                            <img src="/images/conference/expect/mitch.jpg" />
-                            <h4>Mitchel Resnick</h4>
-                            <p>
-                                Professor of Learning Research at the MIT Media Lab
-                            </p>
+const ConferenceExpectations = () => (
+    <div className="expect">
+        <TitleBanner className="mod-conference">
+            <h1>
+                What to Expect
+            </h1>
+            <div className="title-icon">
+                <img
+                    alt="expect-image"
+                    src="/images/conference/expect/what-to-expect.png"
+                />
+            </div>
+        </TitleBanner>
+        <section className="inner profile">
+            <FlexRow className="uneven">
+                <div className="short">
+                    <img src="/images/conference/expect/mitch.jpg" />
+                    <h4>Mitchel Resnick</h4>
+                    <p>
+                        Professor of Learning Research at the MIT Media Lab
+                    </p>
+                </div>
+                <div className="long">
+                    <h2>Welcome to Scratch@MIT 2016!</h2>
+                    <p className="intro">
+                        The Scratch community keeps growing and growing.{' '}
+                        Young people around the world have shared more than{' '}
+                        15 million projects in the Scratch online community{' '}
+                        – with 20,000 new projects every day.
+                    </p>
+                    <p className="intro">
+                        But what excites us most is not the number of projects{' '}
+                        but the diversity of projects. Take a look at the Scratch{' '}
+                        website, and you’ll find an incredible variety of projects:{' '}
+                        musical animations, virtual tours, science simulations,{' '}
+                        interactive tutorials, and much more.
+                    </p>
+                    <p className="intro">
+                        For us, this diversity of projects is an indication that{' '}
+                        members of the Scratch community are developing their own{' '}
+                        voices and identities through Scratch. They are learning{' '}
+                        to express themselves creatively, to build on their interests,{' '}
+                        and to share their ideas with others.
+                    </p>
+                    <p className="intro">
+                        At this year’s Scratch@MIT conference, we’ll celebrate the many{' '}
+                        paths and many styles of Scratch, exploring the multiple ways{' '}
+                        that people can create, share, and learn with Scratch.
+                    </p>
+                    <p className="intro">
+                        We are planning a very participatory conference, with lots of{' '}
+                        hands-on workshops and opportunities for collaboration and sharing.{' '}
+                        Let’s learn together!
+                    </p>
+                </div>
+            </FlexRow>
+        </section>
+        <section className="keynote">
+            <div className="inner">
+                <div className="section-header">
+                    <h2>Keynotes</h2>
+                </div>
+                <FlexRow>
+                    <div className="card">
+                        <div className="date">
+                            <b>Thursday</b>
                         </div>
-                        <div className="long">
-                            <h2>Welcome to Scratch@MIT 2016!</h2>
-                            <p className="intro">
-                                The Scratch community keeps growing and growing.{' '}
-                                Young people around the world have shared more than{' '}
-                                15 million projects in the Scratch online community{' '}
-                                – with 20,000 new projects every day.
-                            </p>
-                            <p className="intro">
-                                But what excites us most is not the number of projects{' '}
-                                but the diversity of projects. Take a look at the Scratch{' '}
-                                website, and you’ll find an incredible variety of projects:{' '}
-                                musical animations, virtual tours, science simulations,{' '}
-                                interactive tutorials, and much more.
-                            </p>
-                            <p className="intro">
-                                For us, this diversity of projects is an indication that{' '}
-                                members of the Scratch community are developing their own{' '}
-                                voices and identities through Scratch. They are learning{' '}
-                                to express themselves creatively, to build on their interests,{' '}
-                                and to share their ideas with others.
-                            </p>
-                            <p className="intro">
-                                At this year’s Scratch@MIT conference, we’ll celebrate the many{' '}
-                                paths and many styles of Scratch, exploring the multiple ways{' '}
-                                that people can create, share, and learn with Scratch.
-                            </p>
-                            <p className="intro">
-                                We are planning a very participatory conference, with lots of{' '}
-                                hands-on workshops and opportunities for collaboration and sharing.{' '}
-                                Let’s learn together!
-                            </p>
-                        </div>
-                    </FlexRow>
-                </section>
-                <section className="keynote">
-                    <div className="inner">
-                        <div className="section-header">
-                            <h2>Keynotes</h2>
-                        </div>
-                        <FlexRow>
-                            <div className="card">
-                                <div className="date">
-                                    <b>Thursday</b>
-                                </div>
-                                <h3>Scratch Conversations</h3>
-                                <img src="/images/conference/expect/scratch-team.jpg" alt="Scratch Team Photo" />
-                                <p>
-                                    <b>MIT Scratch Team</b>
-                                    <br />
-                                    <b>Mitchel Resnick (moderator)</b>
-                                </p>
-                                <p>
-                                    The MIT Scratch Team opens the conference with a series of{' '}
-                                    conversations, exploring new ideas, new directions, and new{' '}
-                                    strategies for supporting creative learning with Scratch.
-                                </p>
-                            </div>
-                            <div className="card">
-                                <div className="date">
-                                    <b>Friday</b>
-                                </div>
-                                <h3>Pathways to Participation</h3>
-                                <img src="/images/conference/expect/mimi-nichole.jpg" alt="Mimi and Nichole" />
-                                <p>
-                                    <b>Mimi Ito &amp; Nichole Pinkard</b>
-                                    <br />
-                                    <b>Ricarose Roque (moderator)</b>
-                                </p>
-                                <p>
-                                    How can we ensure that young people of all backgrounds and all{' '}
-                                    interests have opportunities to learn, grow, and thrive in today’s{' '}
-                                    rapidly-changing digital society?
-                                </p>
-                            </div>
-                            <div className="card">
-                                <div className="date">
-                                    <b>Saturday</b>
-                                </div>
-                                <h3>Creative Computing For All</h3>
-                                <img src="/images/conference/expect/karen.jpg" alt="Karen" />
-                                <p>
-                                    <b>Karen Brennan (moderator)</b>
-                                </p>
-                                <p>
-                                    With ever-increasing attention on computing education and computer{' '}
-                                    science in K–12, what role can teachers play in making creative{' '}
-                                    computing experiences accessible to all learners?
-                                </p>
-                            </div>
-                        </FlexRow>
-                    </div>
-                </section>
-                <section className="inner schedule">
-                    <div className="section-header">
-                        <div className="title">
-                            <h2>Daily Schedules</h2>
-                        </div>
-                        <p className="callout">
-                            <img src="/svgs/conference/expect/aug3-icon.svg" alt="August 3rd Icon" />
-                            <b>Wednesday at 6:00p</b>&nbsp;–&nbsp;Early check-in and opening reception
+                        <h3>Scratch Conversations</h3>
+                        <img
+                            alt="Scratch Team Photo"
+                            src="/images/conference/expect/scratch-team.jpg"
+                        />
+                        <p>
+                            <b>MIT Scratch Team</b>
+                            <br />
+                            <b>Mitchel Resnick (moderator)</b>
+                        </p>
+                        <p>
+                            The MIT Scratch Team opens the conference with a series of{' '}
+                            conversations, exploring new ideas, new directions, and new{' '}
+                            strategies for supporting creative learning with Scratch.
                         </p>
                     </div>
-                    <FlexRow>
-                        <table>
-                            <tbody>
-                                <tr>
-                                    <th>
-                                        <img src="/svgs/conference/expect/aug4-icon.svg" alt="August 4th Icon" />
-                                        <h3>Thursday</h3>
-                                    </th>
-                                </tr>
-                                <tr>
-                                    <td>
-                                        <b>8:30a</b>
-                                        <p>Breakfast (provided)</p>
-                                    </td>
-                                </tr>
-                                <tr>
-                                    <td>
-                                        <b>9:30a</b>
-                                        <p>Keynote Presentation</p>
-                                    </td>
-                                </tr>
-                                <tr>
-                                    <td>
-                                        <b>11:00a</b>
-                                        <p>Morning Workshops</p>
-                                    </td>
-                                </tr>
-                                <tr>
-                                    <td>
-                                        <b>12:30p</b>
-                                        <p>Lunch (provided)</p>
-                                    </td>
-                                </tr>
-                                <tr>
-                                    <td>
-                                        <b>2:00p</b>
-                                        <p>Afternoon Workshops</p>
-                                    </td>
-                                </tr>
-                                <tr>
-                                    <td>
-                                        <b>4:00p</b>
-                                        <p>Poster Sessions</p>
-                                    </td>
-                                </tr>
-                                <tr>
-                                    <td>
-                                        <b>7:00p</b>
-                                        <p>Self-organized dinner excursions</p>
-                                    </td>
-                                </tr>
-                            </tbody>
-                        </table>
-                        <table>
-                            <tbody>
-                                <tr>
-                                    <th>
-                                        <img src="/svgs/conference/expect/aug5-icon.svg" alt="August 5th Icon" />
-                                        <h3>Friday</h3>
-                                    </th>
-                                </tr>
-                                <tr>
-                                    <td>
-                                        <b>8:30a</b>
-                                        <p>Breakfast (provided)</p>
-                                    </td>
-                                </tr>
-                                <tr>
-                                    <td>
-                                        <b>9:30a</b>
-                                        <p>Keynote Presentation</p>
-                                    </td>
-                                </tr>
-                                <tr>
-                                    <td>
-                                        <b>11:00a</b>
-                                        <p>Morning Workshops, Panels, and Ignite Talks</p>
-                                    </td>
-                                </tr>
-                                <tr>
-                                    <td>
-                                        <b>12:00p</b>
-                                        <p>Lunch (provided)</p>
-                                    </td>
-                                </tr>
-                                <tr>
-                                    <td>
-                                        <b>1:30p</b>
-                                        <p>Early afternoon Workshops Panels and Ignite Talks</p>
-                                    </td>
-                                </tr>
-                                <tr>
-                                    <td>
-                                        <b>3:00p</b>
-                                        <p>Late afternoon Workshops, Panels and Ignite Talks</p>
-                                    </td>
-                                </tr>
-                                <tr>
-                                    <td>
-                                        <b>4:30p</b>
-                                        <p>Poster Sessions</p>
-                                    </td>
-                                </tr>
-                                <tr>
-                                    <td>
-                                        <b>6:30p</b>
-                                        <p>Conference Dinner</p>
-                                    </td>
-                                </tr>
-                            </tbody>
-                        </table>
-                        <table>
-                            <tbody>
-                                <tr>
-                                    <th>
-                                        <img src="/svgs/conference/expect/aug6-icon.svg" alt="August 6th Icon" />
-                                        <h3>Saturday</h3>
-                                    </th>
-                                </tr>
-                                <tr>
-                                    <td>
-                                        <b>8:30a</b>
-                                        <p>Breakfast (provided)</p>
-                                    </td>
-                                </tr>
-                                <tr>
-                                    <td>
-                                        <b>9:30a</b>
-                                        <p>Keynote Presentation</p>
-                                    </td>
-                                </tr>
-                                <tr>
-                                    <td>
-                                        <b>11:00a</b>
-                                        <p>Morning Workshops, Panels and Ignite Talks</p>
-                                    </td>
-                                </tr>
-                                <tr>
-                                    <td>
-                                        <b>12:00p</b>
-                                        <p>Lunch (provided) and Wrap-up Session</p>
-                                    </td>
-                                </tr>
-                            </tbody>
-                        </table>
-                    </FlexRow>
-                </section>
+                    <div className="card">
+                        <div className="date">
+                            <b>Friday</b>
+                        </div>
+                        <h3>Pathways to Participation</h3>
+                        <img
+                            alt="Mimi and Nichole"
+                            src="/images/conference/expect/mimi-nichole.jpg"
+                        />
+                        <p>
+                            <b>Mimi Ito &amp; Nichole Pinkard</b>
+                            <br />
+                            <b>Ricarose Roque (moderator)</b>
+                        </p>
+                        <p>
+                            How can we ensure that young people of all backgrounds and all{' '}
+                            interests have opportunities to learn, grow, and thrive in today’s{' '}
+                            rapidly-changing digital society?
+                        </p>
+                    </div>
+                    <div className="card">
+                        <div className="date">
+                            <b>Saturday</b>
+                        </div>
+                        <h3>Creative Computing For All</h3>
+                        <img
+                            alt="Karen"
+                            src="/images/conference/expect/karen.jpg"
+                        />
+                        <p>
+                            <b>Karen Brennan (moderator)</b>
+                        </p>
+                        <p>
+                            With ever-increasing attention on computing education and computer{' '}
+                            science in K–12, what role can teachers play in making creative{' '}
+                            computing experiences accessible to all learners?
+                        </p>
+                    </div>
+                </FlexRow>
             </div>
-        );
-    }
-});
+        </section>
+        <section className="inner schedule">
+            <div className="section-header">
+                <div className="title">
+                    <h2>Daily Schedules</h2>
+                </div>
+                <p className="callout">
+                    <img
+                        alt="August 3rd Icon"
+                        src="/svgs/conference/expect/aug3-icon.svg"
+                    />
+                    <b>Wednesday at 6:00p</b>&nbsp;–&nbsp;Early check-in and opening reception
+                </p>
+            </div>
+            <FlexRow>
+                <table>
+                    <tbody>
+                        <tr>
+                            <th>
+                                <img
+                                    alt="August 4th Icon"
+                                    src="/svgs/conference/expect/aug4-icon.svg"
+                                />
+                                <h3>Thursday</h3>
+                            </th>
+                        </tr>
+                        <tr>
+                            <td>
+                                <b>8:30a</b>
+                                <p>Breakfast (provided)</p>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <b>9:30a</b>
+                                <p>Keynote Presentation</p>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <b>11:00a</b>
+                                <p>Morning Workshops</p>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <b>12:30p</b>
+                                <p>Lunch (provided)</p>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <b>2:00p</b>
+                                <p>Afternoon Workshops</p>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <b>4:00p</b>
+                                <p>Poster Sessions</p>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <b>7:00p</b>
+                                <p>Self-organized dinner excursions</p>
+                            </td>
+                        </tr>
+                    </tbody>
+                </table>
+                <table>
+                    <tbody>
+                        <tr>
+                            <th>
+                                <img
+                                    alt="August 5th Icon"
+                                    src="/svgs/conference/expect/aug5-icon.svg"
+                                />
+                                <h3>Friday</h3>
+                            </th>
+                        </tr>
+                        <tr>
+                            <td>
+                                <b>8:30a</b>
+                                <p>Breakfast (provided)</p>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <b>9:30a</b>
+                                <p>Keynote Presentation</p>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <b>11:00a</b>
+                                <p>Morning Workshops, Panels, and Ignite Talks</p>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <b>12:00p</b>
+                                <p>Lunch (provided)</p>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <b>1:30p</b>
+                                <p>Early afternoon Workshops Panels and Ignite Talks</p>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <b>3:00p</b>
+                                <p>Late afternoon Workshops, Panels and Ignite Talks</p>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <b>4:30p</b>
+                                <p>Poster Sessions</p>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <b>6:30p</b>
+                                <p>Conference Dinner</p>
+                            </td>
+                        </tr>
+                    </tbody>
+                </table>
+                <table>
+                    <tbody>
+                        <tr>
+                            <th>
+                                <img
+                                    alt="August 6th Icon"
+                                    src="/svgs/conference/expect/aug6-icon.svg"
+                                />
+                                <h3>Saturday</h3>
+                            </th>
+                        </tr>
+                        <tr>
+                            <td>
+                                <b>8:30a</b>
+                                <p>Breakfast (provided)</p>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <b>9:30a</b>
+                                <p>Keynote Presentation</p>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <b>11:00a</b>
+                                <p>Morning Workshops, Panels and Ignite Talks</p>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <b>12:00p</b>
+                                <p>Lunch (provided) and Wrap-up Session</p>
+                            </td>
+                        </tr>
+                    </tbody>
+                </table>
+            </FlexRow>
+        </section>
+    </div>
+);
 
 render(<Page><ConferenceExpectations /></Page>, document.getElementById('app'));
diff --git a/src/views/conference/2016/index/index.jsx b/src/views/conference/2016/index/index.jsx
index 91e526668..5a1f5119b 100644
--- a/src/views/conference/2016/index/index.jsx
+++ b/src/views/conference/2016/index/index.jsx
@@ -1,82 +1,88 @@
-var React = require('react');
-var render = require('../../../../lib/render.jsx');
+const React = require('react');
+const render = require('../../../../lib/render.jsx');
 
-var Button = require('../../../../components/forms/button.jsx');
-var FlexRow = require('../../../../components/flex-row/flex-row.jsx');
-var Page = require('../../../../components/page/conference/2016/page.jsx');
-var TitleBanner = require('../../../../components/title-banner/title-banner.jsx');
+const Button = require('../../../../components/forms/button.jsx');
+const FlexRow = require('../../../../components/flex-row/flex-row.jsx');
+const Page = require('../../../../components/page/conference/2016/page.jsx');
+const TitleBanner = require('../../../../components/title-banner/title-banner.jsx');
 
 require('./index.scss');
 
-var ConferenceSplash = React.createClass({
-    type: 'ConferenceSplash',
-
-    render: function () {
-        return (
-            <div className="index mod-2016">
-                <TitleBanner className="mod-conference">
-                    <h1>
-                        Many Paths, Many Styles
-                    </h1>
+const ConferenceSplash = () => (
+    <div className="index mod-2016">
+        <TitleBanner className="mod-conference">
+            <h1>
+                Many Paths, Many Styles
+            </h1>
+            <h3>
+                August 4 - 6, 2016 | Cambridge, MA, USA
+            </h3>
+            <p>
+                <a href="/conference/2016/schedule">
+                    <Button>
+                        See the Schedule
+                    </Button>
+                </a>
+            </p>
+            <p className="sub-button">
+                <b>
+                    <a
+                        href="https://youtu.be/alsfSTVn2es?list=PLpfxVARjkP-8chnTrjtDeo88Pcz6-xf_B"
+                        rel="noopener noreferrer"
+                        target="_blank"
+                    >
+                        Watch videos of the keynote sessions
+                    </a>
+                </b>
+            </p>
+        </TitleBanner>
+        <section className="inner">
+            <FlexRow>
+                <div>
                     <h3>
-                        August 4 - 6, 2016 | Cambridge, MA, USA
+                        <a href="/conference/2016/expect">
+                            <img
+                                alt="expect-image"
+                                src="/images/conference/expect/what-to-expect.png"
+                            />
+                            What to Expect
+                        </a>
                     </h3>
                     <p>
-                        <a href="/conference/2016/schedule">
-                            <Button>
-                                See the Schedule
-                            </Button>
+                        Learn more about participating in Scratch@MIT
+                    </p>
+                </div>
+                <div>
+                    <h3>
+                        <a href="/conference/2016/plan">
+                            <img
+                                alt="plan-image"
+                                src="/images/conference/plan/plan-your-visit.png"
+                            />
+                            Plan Your Visit
                         </a>
+                    </h3>
+                    <p>
+                        Information on traveling, staying, and exploring around the Media Lab
                     </p>
-                    <p className="sub-button">
-                        <b>
-                            <a href="https://youtu.be/alsfSTVn2es?list=PLpfxVARjkP-8chnTrjtDeo88Pcz6-xf_B"
-                                target="_blank">
-                                Watch videos of the keynote sessions
-                            </a>
-                        </b>
+                </div>
+                <div>
+                    <h3>
+                        <a href="/conference/2016/schedule">
+                            <img
+                                alt="schedule"
+                                src="/images/conference/schedule/schedule.png"
+                            />
+                            Schedule
+                        </a>
+                    </h3>
+                    <p>
+                        Full schedule of events and sessions
                     </p>
-                </TitleBanner>
-                <section className="inner">
-                    <FlexRow>
-                        <div>
-                            <h3>
-                                <a href="/conference/2016/expect">
-                                    <img src="/images/conference/expect/what-to-expect.png" alt="expect-image" />
-                                    What to Expect
-                                </a>
-                            </h3>
-                            <p>
-                                Learn more about participating in Scratch@MIT
-                            </p>
-                        </div>
-                        <div>
-                            <h3>
-                                <a href="/conference/2016/plan">
-                                    <img src="/images/conference/plan/plan-your-visit.png" alt="plan-image" />
-                                    Plan Your Visit
-                                </a>
-                            </h3>
-                            <p>
-                                Information on traveling, staying, and exploring around the Media Lab
-                            </p>
-                        </div>
-                        <div>
-                            <h3>
-                                <a href="/conference/2016/schedule">
-                                    <img src="/images/conference/schedule/schedule.png" alt="schedule" />
-                                    Schedule
-                                </a>
-                            </h3>
-                            <p>
-                                Full schedule of events and sessions
-                            </p>
-                        </div>
-                    </FlexRow>
-                </section>
-            </div>
-        );
-    }
-});
+                </div>
+            </FlexRow>
+        </section>
+    </div>
+);
 
 render(<Page><ConferenceSplash /></Page>, document.getElementById('app'));
diff --git a/src/views/conference/2016/plan/plan.jsx b/src/views/conference/2016/plan/plan.jsx
index b680f7bf1..1b5931630 100644
--- a/src/views/conference/2016/plan/plan.jsx
+++ b/src/views/conference/2016/plan/plan.jsx
@@ -1,24 +1,29 @@
-var React = require('react');
-var render = require('../../../../lib/render.jsx');
+const bindAll = require('lodash.bindall');
+const React = require('react');
 
-var Button = require('../../../../components/forms/button.jsx');
-var FlexRow = require('../../../../components/flex-row/flex-row.jsx');
-var Page = require('../../../../components/page/conference/2016/page.jsx');
-var TitleBanner = require('../../../../components/title-banner/title-banner.jsx');
+const Button = require('../../../../components/forms/button.jsx');
+const FlexRow = require('../../../../components/flex-row/flex-row.jsx');
+const TitleBanner = require('../../../../components/title-banner/title-banner.jsx');
+
+const Page = require('../../../../components/page/conference/2016/page.jsx');
+const render = require('../../../../lib/render.jsx');
 
 require('./plan.scss');
 
-var ConferencePlan = React.createClass({
-    type: 'ConferencePlan',
-    getInitialState: function () {
-        return {
+class ConferencePlan extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'toggleQuestion'
+        ]);
+        this.state = {
             dorm: false
         };
-    },
-    toggleQuestion: function (element) {
+    }
+    toggleQuestion (element) {
         this.setState({element: !this.state[element]});
-    },
-    render: function () {
+    }
+    render () {
         return (
             <div className="plan">
                 <TitleBanner className="mod-conference">
@@ -26,7 +31,10 @@ var ConferencePlan = React.createClass({
                         Plan Your Visit
                     </h1>
                     <div className="title-icon">
-                        <img src="/images/conference/plan/plan-your-visit.png" alt="plan-image" />
+                        <img
+                            alt="plan-image"
+                            src="/images/conference/plan/plan-your-visit.png"
+                        />
                     </div>
                 </TitleBanner>
                 <div className="inner">
@@ -73,7 +81,7 @@ var ConferencePlan = React.createClass({
                                 </FlexRow>
                                 <p>
                                     To reserve a room, <b>call the hotel and request the{' '}
-                                    "MIT discount"</b> (subject to availability).
+                                    &#34;MIT discount&#34;</b> (subject to availability).
                                 </p>
                                 <p>
                                     We also suggest the{' '}
@@ -104,7 +112,10 @@ var ConferencePlan = React.createClass({
                                 </p>
                             </div>
                             <div className="short">
-                                <img src="/images/conference/plan/lodging.png" alt="Lodging Illustration" />
+                                <img
+                                    alt="Lodging Illustration"
+                                    src="/images/conference/plan/lodging.png"
+                                />
                             </div>
                         </FlexRow>
                     </section>
@@ -134,8 +145,10 @@ var ConferencePlan = React.createClass({
                                 </p>
                             </div>
                             <div className="short">
-                                <img src="/images/conference/plan/transportation.png"
-                                     alt="Transportation Illustration" />
+                                <img
+                                    alt="Transportation Illustration"
+                                    src="/images/conference/plan/transportation.png"
+                                />
                             </div>
                         </FlexRow>
                     </section>
@@ -159,7 +172,7 @@ var ConferencePlan = React.createClass({
                                 </li>
                                 <li>
                                     <a href="http://www.bostonteapartyship.com/">
-                                        Boston Tea Party Ship & Museum
+                                        Boston Tea Party Ship &amp; Museum
                                     </a>
                                 </li>
                                 <li>
@@ -189,7 +202,7 @@ var ConferencePlan = React.createClass({
                                 </li>
                                 <li>
                                     <a href="http://www.jfklibrary.org/">
-                                        John F. Kennedy Library & Museum
+                                        John F. Kennedy Library &amp; Museum
                                     </a>
                                 </li>
                                 <li>
@@ -320,7 +333,7 @@ var ConferencePlan = React.createClass({
                                     </dt>
                                     <dd>
                                         Contact us at a conference@scratch.mit.edu with your registration number,{' '}
-                                        and we'll take care of it for you.
+                                        and we&#39;ll take care of it for you.
                                     </dd>
                                 </dl>
                             </div>
@@ -336,6 +349,6 @@ var ConferencePlan = React.createClass({
             </div>
         );
     }
-});
+}
 
 render(<Page><ConferencePlan /></Page>, document.getElementById('app'));
diff --git a/src/views/conference/2016/schedule/schedule.jsx b/src/views/conference/2016/schedule/schedule.jsx
index 71a9a8ef9..2c0ee61d4 100644
--- a/src/views/conference/2016/schedule/schedule.jsx
+++ b/src/views/conference/2016/schedule/schedule.jsx
@@ -1,86 +1,124 @@
-var classNames = require('classnames');
-var connect = require('react-redux').connect;
-var React = require('react');
-var render = require('../../../../lib/render.jsx');
+const bindAll = require('lodash.bindall');
+const classNames = require('classnames');
+const connect = require('react-redux').connect;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var scheduleActions = require('../../../../redux/conference-schedule.js');
+const scheduleActions = require('../../../../redux/conference-schedule.js');
 
-var FlexRow = require('../../../../components/flex-row/flex-row.jsx');
-var Page = require('../../../../components/page/conference/2016/page.jsx');
-var SubNavigation = require('../../../../components/subnavigation/subnavigation.jsx');
-var TitleBanner = require('../../../../components/title-banner/title-banner.jsx');
+const FlexRow = require('../../../../components/flex-row/flex-row.jsx');
+const SubNavigation = require('../../../../components/subnavigation/subnavigation.jsx');
+const TitleBanner = require('../../../../components/title-banner/title-banner.jsx');
+
+const Page = require('../../../../components/page/conference/2016/page.jsx');
+const render = require('../../../../lib/render.jsx');
 
 require('./schedule.scss');
 
-var ConferenceSchedule = React.createClass({
-    type: 'ConferenceSchedule',
-    propTypes: {
-        conferenceSchedule: React.PropTypes.object
-    },
-    componentDidMount: function () {
-        var day = window.location.hash.substr(1) || 'thursday';
+class ConferenceSchedule extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleScheduleChange',
+            'renderChunkItems'
+        ]);
+    }
+    componentDidMount () {
+        const day = window.location.hash.substr(1) || 'thursday';
         this.handleScheduleChange(day);
-    },
-    handleScheduleChange: function (day) {
-        window.history.replaceState(history.state, '', '#' + day);
+    }
+    handleScheduleChange (day) {
+        window.history.replaceState(history.state, '', `#${day}`);
         this.props.dispatch(scheduleActions.startGetSchedule(day));
-    },
-    renderChunkItems: function (timeSlot) {
-        return timeSlot.map(function (item) {
+    }
+    renderChunkItems (timeSlot) {
+        return timeSlot.map(item => {
             if (item.Presenter) {
                 return (
-                    <a href={item.uri} className="item-url">
-                        <div key={item.rowid} className="agenda-item">
+                    <a
+                        className="item-url"
+                        href={item.uri}
+                    >
+                        <div
+                            className="agenda-item"
+                            key={item.rowid}
+                        >
                             <h3>{item.Title}</h3>
                             <FlexRow>
                                 <p>
-                                    <img src="/svgs/conference/schedule/time-icon.svg" alt="time icon" />
+                                    <img
+                                        alt="time icon"
+                                        src="/svgs/conference/schedule/time-icon.svg"
+                                    />
                                     {item.Start} &ndash; {item.End}
                                 </p>
                                 <p>
-                                    <img src="/svgs/conference/schedule/location-icon.svg" alt="location icon" />
+                                    <img
+                                        alt="location icon"
+                                        src="/svgs/conference/schedule/location-icon.svg"
+                                    />
                                     {item.Location}
                                 </p>
                             </FlexRow>
                             <FlexRow>
                                 <p>
-                                    <img src="/svgs/conference/schedule/presenter-icon.svg" alt="presenter icon" />
+                                    <img
+                                        alt="presenter icon"
+                                        src="/svgs/conference/schedule/presenter-icon.svg"
+                                    />
                                     {item.Presenter}
                                 </p>
                                 <p>
-                                    <img src="/svgs/conference/schedule/event-icon.svg" alt="event icon" />
+                                    <img
+                                        alt="event icon"
+                                        src="/svgs/conference/schedule/event-icon.svg"
+                                    />
                                     {item.Type}
                                 </p>
                             </FlexRow>
                         </div>
                     </a>
                 );
-            } else {
-                return (
-                    <div key={item.rowid} className="agenda-item no-click">
-                        <h3>{item.Title}</h3>
-                        <FlexRow>
-                            <p>{item.Start} &ndash; {item.End}</p>
-                            <p>{item.Location}</p>
-                        </FlexRow>
-                    </div>
-                );
             }
+            return (
+                <div
+                    className="agenda-item no-click"
+                    key={item.rowid}
+                >
+                    <h3>{item.Title}</h3>
+                    <FlexRow>
+                        <p>{item.Start} &ndash; {item.End}</p>
+                        <p>{item.Location}</p>
+                    </FlexRow>
+                </div>
+            );
         });
-    },
-    render: function () {
-        var tabClasses = {
-            'thursday': classNames({
-                'selected': (this.props.conferenceSchedule.day === 'thursday')
+    }
+    render () {
+        const tabClasses = {
+            thursday: classNames({
+                selected: (this.props.conferenceSchedule.day === 'thursday')
             }),
-            'friday': classNames({
-                'selected': (this.props.conferenceSchedule.day === 'friday')
+            friday: classNames({
+                selected: (this.props.conferenceSchedule.day === 'friday')
             }),
-            'saturday': classNames({
-                'last': true,
-                'selected': (this.props.conferenceSchedule.day === 'saturday')
+            saturday: classNames({
+                last: true,
+                selected: (this.props.conferenceSchedule.day === 'saturday')
             })
         };
+        const handleScheduleMethods = {
+            thursday: () => {
+                this.handleScheduleChange('thursday');
+            },
+            friday: () => {
+                this.handleScheduleChange('friday');
+            },
+            saturday: () => {
+                this.handleScheduleChange('saturday');
+            }
+        };
+
         return (
             <div className="schedule">
                 <TitleBanner className="mod-conference">
@@ -89,44 +127,63 @@ var ConferenceSchedule = React.createClass({
                     </h1>
                 </TitleBanner>
                 <SubNavigation>
-                    <li className={tabClasses.thursday}
-                        onClick={this.handleScheduleChange.bind(this, 'thursday')}>
-                        <img src="/svgs/conference/expect/aug4-icon.svg" alt="August 4th Icon" />
+                    <li
+                        className={tabClasses.thursday}
+                        onClick={handleScheduleMethods.thursday}
+                    >
+                        <img
+                            alt="August 4th Icon"
+                            src="/svgs/conference/expect/aug4-icon.svg"
+                        />
                         <span>Thursday</span>
                     </li>
-                    <li className={tabClasses.friday}
-                        onClick={this.handleScheduleChange.bind(this, 'friday')}>
-                        <img src="/svgs/conference/expect/aug5-icon.svg" alt="August 5th Icon" />
+                    <li
+                        className={tabClasses.friday}
+                        onClick={handleScheduleMethods.friday}
+                    >
+                        <img
+                            alt="August 5th Icon"
+                            src="/svgs/conference/expect/aug5-icon.svg"
+                        />
                         <span>Friday</span>
                     </li>
-                    <li className={tabClasses.saturday}
-                        onClick={this.handleScheduleChange.bind(this, 'saturday')}>
-                        <img src="/svgs/conference/expect/aug6-icon.svg" alt="August 6th Icon" />
+                    <li
+                        className={tabClasses.saturday}
+                        onClick={handleScheduleMethods.saturday}
+                    >
+                        <img
+                            alt="August 6th Icon"
+                            src="/svgs/conference/expect/aug6-icon.svg"
+                        />
                         <span>Saturday</span>
                     </li>
                 </SubNavigation>
                 <div className="inner">
-                    {this.props.conferenceSchedule.timeSlots.map(function (timeSlot) {
-                        return ([
-                            <h2 key={timeSlot.info.name} className="breaking-title">
-                                <span>{timeSlot.info.name} – {timeSlot.info.time}</span>
-                            </h2>,
-                            this.renderChunkItems(timeSlot.items)
-                        ]);
-                    }.bind(this))}
+                    {this.props.conferenceSchedule.timeSlots.map(timeSlot => ([
+                        <h2
+                            className="breaking-title"
+                            key={timeSlot.info.name}
+                        >
+                            <span>{timeSlot.info.name} – {timeSlot.info.time}</span>
+                        </h2>,
+                        this.renderChunkItems(timeSlot.items)
+                    ]))}
                 </div>
             </div>
         );
     }
-});
+}
 
-var mapStateToProps = function (state) {
-    return {
-        conferenceSchedule: state.conferenceSchedule
-    };
+ConferenceSchedule.propTypes = {
+    conferenceSchedule: PropTypes.object, // eslint-disable-line react/forbid-prop-types
+    dispatch: PropTypes.func
 };
 
-var ConnectedSchedule = connect(mapStateToProps)(ConferenceSchedule);
+const mapStateToProps = state => ({
+    conferenceSchedule: state.conferenceSchedule
+});
+
+const ConnectedSchedule = connect(mapStateToProps)(ConferenceSchedule);
 
 render(
     <Page><ConnectedSchedule /></Page>,
diff --git a/src/views/conference/2017/index/index.jsx b/src/views/conference/2017/index/index.jsx
index 2717f2b9a..98fa49c54 100644
--- a/src/views/conference/2017/index/index.jsx
+++ b/src/views/conference/2017/index/index.jsx
@@ -1,514 +1,526 @@
-var FormattedDate = require('react-intl').FormattedDate;
-var FormattedMessage = require('react-intl').FormattedMessage;
-var React = require('react');
-var render = require('../../../../lib/render.jsx');
+const FormattedDate = require('react-intl').FormattedDate;
+const FormattedMessage = require('react-intl').FormattedMessage;
+const React = require('react');
+const render = require('../../../../lib/render.jsx');
 
-var FlexRow = require('../../../../components/flex-row/flex-row.jsx');
-var Page = require('../../../../components/page/conference/2017/page.jsx');
-var TitleBanner = require('../../../../components/title-banner/title-banner.jsx');
+const FlexRow = require('../../../../components/flex-row/flex-row.jsx');
+const Page = require('../../../../components/page/conference/2017/page.jsx');
+const TitleBanner = require('../../../../components/title-banner/title-banner.jsx');
 
 require('../../../../components/forms/button.scss');
 require('./index.scss');
 
-var ConferenceSplash = React.createClass({
-    type: 'ConferenceSplash',
-
-    render: function () {
-        return (
-            <div className='index mod-2017'>
-                <TitleBanner className='mod-conference mod-2017'>
-                    <div className='title-banner-image mod-2017'></div>
-                    <h1 className='title-banner-h1 mod-2017'>
-                        <FormattedMessage id='conference-2017.title' />
-                    </h1>
-                    <h3 className='title-banner-h3 mod-2017'>
-                        <FormattedMessage id='conference-2017.desc' />
-                    </h3>
-                </TitleBanner>
-                <h3 className='conf2017-title-band'>
-                    <FormattedMessage id='conference-2017.seeBelow' />
-                </h3>
-                <div className='inner'>
-                    <section className='conf2017-panel mod-france'>
-                        <FlexRow className='conf2017-panel-title'>
-                            <img
-                                className='conf2017-panel-flag'
-                                src='/svgs/conference/flags/fr.svg'
-                                alt='France Flag'
-                            />
-                            <div className='conf2017-panel-title-text'>
-                                <h3><FormattedMessage id='conference-2017.franceTitle' /></h3>
-                                <h4><FormattedMessage id='conference-2017.franceSubTitle' /></h4>
-                            </div>
-                        </FlexRow>
-                        <p className='conf2017-panel-desc'>
-                            <FormattedMessage id='conference-2017.franceDesc' />
-                        </p>
-                        <table className='conf2017-panel-details'>
-                            <tbody>
-                                <tr className='conf2017-panel-row'>
-                                    <td className='conf2017-panel-row-icon'>
-                                        <img
-                                            className='conf2017-panel-row-icon-image'
-                                            src='/svgs/conference/index/calendar-icon.svg'
-                                            alt='Calendar Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2017.date' /></td>
-                                    <td>
-                                        <FormattedDate
-                                            value={new Date(2017, 6, 18)}
-                                            year='numeric'
-                                            month='long'
-                                            day='2-digit'
-                                        />
-                                        {' - '}
-                                        <FormattedDate
-                                            value={new Date(2017, 6, 21)}
-                                            year='numeric'
-                                            month='long'
-                                            day='2-digit'
-                                        />
-                                    </td>
-                                </tr>
-                                <tr className='conf2017-panel-row'>
-                                    <td className='conf2017-panel-row-icon'>
-                                        <img
-                                            className='conf2017-panel-row-icon-image'
-                                            src='/svgs/conference/index/map-icon.svg'
-                                            alt='Map Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2017.location' /></td>
-                                    <td>{'Bordeaux, France'}</td>
-                                </tr>
-                                <tr className='conf2017-panel-row'>
-                                    <td className='conf2017-panel-row-icon'>
-                                        <img
-                                            className='conf2017-panel-row-icon-image'
-                                            src='/svgs/conference/index/audience-icon.svg'
-                                            alt='Audience Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2017.audience' /></td>
-                                    <td><FormattedMessage id='conference-2017.franceAudience' /></td>
-                                </tr>
-                                <tr className='conf2017-panel-row'>
-                                    <td className='conf2017-panel-row-icon'>
-                                        <img
-                                            className='conf2017-panel-row-icon-image'
-                                            src='/svgs/conference/index/language-icon.svg'
-                                            alt='Language Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2017.language' /></td>
-                                    <td>{'English'}</td>
-                                </tr>
-                            </tbody>
-                        </table>
-                        <a className='button mod-2017-panel' href='http://scratch2017bdx.org'>
-                            <FormattedMessage id='conference-2017.website' />
-                        </a>
-                    </section>
-                    <section className='conf2017-panel mod-hungary'>
-                        <FlexRow className='conf2017-panel-title'>
-                            <img
-                                className='conf2017-panel-flag'
-                                src='/svgs/conference/flags/hu.svg'
-                                alt='Hungary Flag'
-                            />
-                            <div className='conf2017-panel-title-text'>
-                                <h3><FormattedMessage id='conference-2017.hungaryTitle' /></h3>
-                            </div>
-                        </FlexRow>
-                        <p className='conf2017-panel-desc'>
-                            <FormattedMessage id='conference-2017.hungaryDesc' />
-                        </p>
-                        <table className='conf2017-panel-details'>
-                            <tbody>
-                                <tr className='conf2017-panel-row'>
-                                    <td className='conf2017-panel-row-icon'>
-                                        <img
-                                            className='conf2017-panel-row-icon-image'
-                                            src='/svgs/conference/index/calendar-icon.svg'
-                                            alt='Calendar Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2017.date' /></td>
-                                    <td>
-                                        <FormattedDate
-                                            value={new Date(2017, 7, 24)}
-                                            year='numeric'
-                                            month='long'
-                                            day='2-digit'
-                                        />
-                                        {' - '}
-                                        <FormattedDate
-                                            value={new Date(2017, 7, 25)}
-                                            year='numeric'
-                                            month='long'
-                                            day='2-digit'
-                                        />
-                                    </td>
-                                </tr>
-                                <tr className='conf2017-panel-row'>
-                                    <td className='conf2017-panel-row-icon'>
-                                        <img
-                                            className='conf2017-panel-row-icon-image'
-                                            src='/svgs/conference/index/map-icon.svg'
-                                            alt='Map Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2017.location' /></td>
-                                    <td>{'Budapest, Hungary'}</td>
-                                </tr>
-                                <tr className='conf2017-panel-row'>
-                                    <td className='conf2017-panel-row-icon'>
-                                        <img
-                                            className='conf2017-panel-row-icon-image'
-                                            src='/svgs/conference/index/audience-icon.svg'
-                                            alt='Audience Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2017.audience' /></td>
-                                    <td><FormattedMessage id='conference-2017.hungaryAudience' /></td>
-                                </tr>
-                                <tr className='conf2017-panel-row'>
-                                    <td className='conf2017-panel-row-icon'>
-                                        <img
-                                            className='conf2017-panel-row-icon-image'
-                                            src='/svgs/conference/index/language-icon.svg'
-                                            alt='Language Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2017.language' /></td>
-                                    <td>{'English'}</td>
-                                </tr>
-                            </tbody>
-                        </table>
-                        <a className='button mod-2017-panel' href='https://events.epam.com/events/scratch-2017'>
-                            <FormattedMessage id='conference-2017.website' />
-                        </a>
-                    </section>
-                    <section className='conf2017-panel mod-costarica'>
-                        <FlexRow className='conf2017-panel-title'>
-                            <img
-                                className='conf2017-panel-flag'
-                                src='/svgs/conference/flags/cr.svg'
-                                alt='Costa Rica Flag'
-                            />
-                            <div className='conf2017-panel-title-text'>
-                                <h3><FormattedMessage id='conference-2017.costaricaTitle' /></h3>
-                                <h4><FormattedMessage id='conference-2017.costaricaSubTitle' /></h4>
-                            </div>
-                        </FlexRow>
-                        <p className='conf2017-panel-desc'>
-                            <FormattedMessage id='conference-2017.costaricaDesc' />
-                        </p>
-                        <table className='conf2017-panel-details'>
-                            <tbody>
-                                <tr className='conf2017-panel-row'>
-                                    <td className='conf2017-panel-row-icon'>
-                                        <img
-                                            className='conf2017-panel-row-icon-image'
-                                            src='/svgs/conference/index/calendar-icon.svg'
-                                            alt='Calendar Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2017.date' /></td>
-                                    <td>
-                                        <FormattedDate
-                                            value={new Date(2017, 10, 12)}
-                                            year='numeric'
-                                            month='long'
-                                            day='2-digit'
-                                        />
-                                    </td>
-                                </tr>
-                                <tr className='conf2017-panel-row'>
-                                    <td className='conf2017-panel-row-icon'>
-                                        <img
-                                            className='conf2017-panel-row-icon-image'
-                                            src='/svgs/conference/index/map-icon.svg'
-                                            alt='Map Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2017.location' /></td>
-                                    <td>{'San José, Costa Rica'}</td>
-                                </tr>
-                                <tr className='conf2017-panel-row'>
-                                    <td className='conf2017-panel-row-icon'>
-                                        <img
-                                            className='conf2017-panel-row-icon-image'
-                                            src='/svgs/conference/index/audience-icon.svg'
-                                            alt='Audience Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2017.audience' /></td>
-                                    <td><FormattedMessage id='conference-2017.costaricaAudience' /></td>
-                                </tr>
-                                <tr className='conf2017-panel-row'>
-                                    <td className='conf2017-panel-row-icon'>
-                                        <img
-                                            className='conf2017-panel-row-icon-image'
-                                            src='/svgs/conference/index/language-icon.svg'
-                                            alt='Language Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2017.language' /></td>
-                                    <td>{'Español (Spanish)'}</td>
-                                </tr>
-                            </tbody>
-                        </table>
-                        <a className='button mod-2017-panel' href='https://scratchcostarica.com/'>
-                            <FormattedMessage id='conference-2017.website' />
-                        </a>
-                    </section>
-                    <section className='conf2017-panel mod-chile'>
-                        <FlexRow className='conf2017-panel-title'>
-                            <img
-                                className='conf2017-panel-flag'
-                                src='/svgs/conference/flags/cl.svg'
-                                alt='Chile Flag'
-                            />
-                            <div className='conf2017-panel-title-text'>
-                                <h3><FormattedMessage id='conference-2017.chileTitle' /></h3>
-                                <h4><FormattedMessage id='conference-2017.chileSubTitle' /></h4>
-                            </div>
-                        </FlexRow>
-                        <p className='conf2017-panel-desc'>
-                            <FormattedMessage id='conference-2017.chileDesc' />
-                        </p>
-                        <table className='conf2017-panel-details'>
-                            <tbody>
-                                <tr className='conf2017-panel-row'>
-                                    <td className='conf2017-panel-row-icon'>
-                                        <img
-                                            className='conf2017-panel-row-icon-image'
-                                            src='/svgs/conference/index/calendar-icon.svg'
-                                            alt='Calendar Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2017.date' /></td>
-                                    <td>
-                                        <FormattedDate
-                                            value={new Date(2017, 7, 31)}
-                                            year='numeric'
-                                            month='long'
-                                            day='2-digit'
-                                        />
-                                        {' - '}
-                                        <FormattedDate
-                                            value={new Date(2017, 8, 1)}
-                                            year='numeric'
-                                            month='long'
-                                            day='2-digit'
-                                        />
-                                    </td>
-                                </tr>
-                                <tr className='conf2017-panel-row'>
-                                    <td className='conf2017-panel-row-icon'>
-                                        <img
-                                            className='conf2017-panel-row-icon-image'
-                                            src='/svgs/conference/index/map-icon.svg'
-                                            alt='Map Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2017.location' /></td>
-                                    <td>{'Santiago, Chile'}</td>
-                                </tr>
-                                <tr className='conf2017-panel-row'>
-                                    <td className='conf2017-panel-row-icon'>
-                                        <img
-                                            className='conf2017-panel-row-icon-image'
-                                            src='/svgs/conference/index/audience-icon.svg'
-                                            alt='Audience Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2017.audience' /></td>
-                                    <td><FormattedMessage id='conference-2017.chileAudience' /></td>
-                                </tr>
-                                <tr className='conf2017-panel-row'>
-                                    <td className='conf2017-panel-row-icon'>
-                                        <img
-                                            className='conf2017-panel-row-icon-image'
-                                            src='/svgs/conference/index/language-icon.svg'
-                                            alt='Language Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2017.language' /></td>
-                                    <td>{'Español (Spanish) - simultaneous translation during plenary sessions'}</td>
-                                </tr>
-                            </tbody>
-                        </table>
-                        <a className='button mod-2017-panel' href='http://www.scratchalsur.org'>
-                            <FormattedMessage id='conference-2017.website' />
-                        </a>
-                    </section>
-                    <section className='conf2017-panel mod-brasil'>
-                        <FlexRow className='conf2017-panel-title'>
-                            <img
-                                className='conf2017-panel-flag'
-                                src='/svgs/conference/flags/br.svg'
-                                alt='Brasil Flag'
-                            />
-                            <div className='conf2017-panel-title-text'>
-                                <h3><FormattedMessage id='conference-2017.brasilTitle' /></h3>
-                            </div>
-                        </FlexRow>
-                        <p className='conf2017-panel-desc'>
-                            <FormattedMessage id='conference-2017.brasilDesc' />
-                        </p>
-                        <table className='conf2017-panel-details'>
-                            <tbody>
-                                <tr className='conf2017-panel-row'>
-                                    <td className='conf2017-panel-row-icon'>
-                                        <img
-                                            className='conf2017-panel-row-icon-image'
-                                            src='/svgs/conference/index/calendar-icon.svg'
-                                            alt='Calendar Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2017.date' /></td>
-                                    <td>
-                                        <FormattedDate
-                                            value={new Date(2017, 9, 5)}
-                                            year='numeric'
-                                            month='long'
-                                            day='2-digit'
-                                        />
-                                        {' - '}
-                                        <FormattedDate
-                                            value={new Date(2017, 9, 7)}
-                                            year='numeric'
-                                            month='long'
-                                            day='2-digit'
-                                        />
-                                    </td>
-                                </tr>
-                                <tr className='conf2017-panel-row'>
-                                    <td className='conf2017-panel-row-icon'>
-                                        <img
-                                            className='conf2017-panel-row-icon-image'
-                                            src='/svgs/conference/index/map-icon.svg'
-                                            alt='Map Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2017.location' /></td>
-                                    <td>{'São Paulo, Brasil'}</td>
-                                </tr>
-                                <tr className='conf2017-panel-row'>
-                                    <td className='conf2017-panel-row-icon'>
-                                        <img
-                                            className='conf2017-panel-row-icon-image'
-                                            src='/svgs/conference/index/audience-icon.svg'
-                                            alt='Audience Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2017.audience' /></td>
-                                    <td><FormattedMessage id='conference-2017.brasilAudience' /></td>
-                                </tr>
-                                <tr className='conf2017-panel-row'>
-                                    <td className='conf2017-panel-row-icon'>
-                                        <img
-                                            className='conf2017-panel-row-icon-image'
-                                            src='/svgs/conference/index/language-icon.svg'
-                                            alt='Language Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2017.language' /></td>
-                                    <td>{'Português (Portuguese)'}</td>
-                                </tr>
-                            </tbody>
-                        </table>
-                        <a className='button mod-2017-panel' href='http://scratchbrasil.org/'>
-                            <FormattedMessage id='conference-2017.website' />
-                        </a>
-                    </section>
-                    <section className='conf2017-panel mod-china mod-last'>
-                        <FlexRow className='conf2017-panel-title'>
-                            <img
-                                className='conf2017-panel-flag'
-                                src='/svgs/conference/flags/cn.svg'
-                                alt='China Flag'
-                            />
-                            <div className='conf2017-panel-title-text'>
-                                <h3><FormattedMessage id='conference-2017.chinaTitle' /></h3>
-                            </div>
-                        </FlexRow>
-                        <p className='conf2017-panel-desc'>
-                            <FormattedMessage id='conference-2017.chinaDesc' />
-                        </p>
-                        <table className='conf2017-panel-details'>
-                            <tbody>
-                                <tr className='conf2017-panel-row'>
-                                    <td className='conf2017-panel-row-icon'>
-                                        <img
-                                            className='conf2017-panel-row-icon-image'
-                                            src='/svgs/conference/index/calendar-icon.svg'
-                                            alt='Calendar Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2017.date' /></td>
-                                    <td>
-                                        <FormattedDate
-                                            value={new Date(2017, 4, 20)}
-                                            year='numeric'
-                                            month='long'
-                                            day='2-digit'
-                                        />
-                                        {' - '}
-                                        <FormattedDate
-                                            value={new Date(2017, 4, 21)}
-                                            year='numeric'
-                                            month='long'
-                                            day='2-digit'
-                                        />
-                                    </td>
-                                </tr>
-                                <tr className='conf2017-panel-row'>
-                                    <td className='conf2017-panel-row-icon'>
-                                        <img
-                                            className='conf2017-panel-row-icon-image'
-                                            src='/svgs/conference/index/map-icon.svg'
-                                            alt='Map Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2017.location' /></td>
-                                    <td>{'Shanghai, China'}</td>
-                                </tr>
-                                <tr className='conf2017-panel-row'>
-                                    <td className='conf2017-panel-row-icon'>
-                                        <img
-                                            className='conf2017-panel-row-icon-image'
-                                            src='/svgs/conference/index/audience-icon.svg'
-                                            alt='Audience Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2017.audience' /></td>
-                                    <td><FormattedMessage id='conference-2017.chinaAudience' /></td>
-                                </tr>
-                                <tr className='conf2017-panel-row'>
-                                    <td className='conf2017-panel-row-icon'>
-                                        <img
-                                            className='conf2017-panel-row-icon-image'
-                                            src='/svgs/conference/index/language-icon.svg'
-                                            alt='Language Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2017.language' /></td>
-                                    <td>{'中文 (Chinese)'}</td>
-                                </tr>
-                            </tbody>
-                        </table>
-                        <a className='button mod-2017-panel' href='http://scratchconference2017.sxl.cn/'>
-                            <FormattedMessage id='conference-2017.website' />
-                        </a>
-                    </section>
-                </div>
-            </div>
-        );
-    }
-});
+const ConferenceSplash = () => (
+    <div className="index mod-2017">
+        <TitleBanner className="mod-conference mod-2017">
+            <div className="title-banner-image mod-2017" />
+            <h1 className="title-banner-h1 mod-2017">
+                <FormattedMessage id="conference-2017.title" />
+            </h1>
+            <h3 className="title-banner-h3 mod-2017">
+                <FormattedMessage id="conference-2017.desc" />
+            </h3>
+        </TitleBanner>
+        <h3 className="conf2017-title-band">
+            <FormattedMessage id="conference-2017.seeBelow" />
+        </h3>
+        <div className="inner">
+            <section className="conf2017-panel mod-france">
+                <FlexRow className="conf2017-panel-title">
+                    <img
+                        alt="France Flag"
+                        className="conf2017-panel-flag"
+                        src="/svgs/conference/flags/fr.svg"
+                    />
+                    <div className="conf2017-panel-title-text">
+                        <h3><FormattedMessage id="conference-2017.franceTitle" /></h3>
+                        <h4><FormattedMessage id="conference-2017.franceSubTitle" /></h4>
+                    </div>
+                </FlexRow>
+                <p className="conf2017-panel-desc">
+                    <FormattedMessage id="conference-2017.franceDesc" />
+                </p>
+                <table className="conf2017-panel-details">
+                    <tbody>
+                        <tr className="conf2017-panel-row">
+                            <td className="conf2017-panel-row-icon">
+                                <img
+                                    alt="Calendar Icon"
+                                    className="conf2017-panel-row-icon-image"
+                                    src="/svgs/conference/index/calendar-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2017.date" /></td>
+                            <td>
+                                <FormattedDate
+                                    day="2-digit"
+                                    month="long"
+                                    value={new Date(2017, 6, 18)}
+                                    year="numeric"
+                                />
+                                {' - '}
+                                <FormattedDate
+                                    day="2-digit"
+                                    month="long"
+                                    value={new Date(2017, 6, 21)}
+                                    year="numeric"
+                                />
+                            </td>
+                        </tr>
+                        <tr className="conf2017-panel-row">
+                            <td className="conf2017-panel-row-icon">
+                                <img
+                                    alt="Map Icon"
+                                    className="conf2017-panel-row-icon-image"
+                                    src="/svgs/conference/index/map-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2017.location" /></td>
+                            <td>{'Bordeaux, France'}</td>
+                        </tr>
+                        <tr className="conf2017-panel-row">
+                            <td className="conf2017-panel-row-icon">
+                                <img
+                                    alt="Audience Icon"
+                                    className="conf2017-panel-row-icon-image"
+                                    src="/svgs/conference/index/audience-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2017.audience" /></td>
+                            <td><FormattedMessage id="conference-2017.franceAudience" /></td>
+                        </tr>
+                        <tr className="conf2017-panel-row">
+                            <td className="conf2017-panel-row-icon">
+                                <img
+                                    alt="Language Icon"
+                                    className="conf2017-panel-row-icon-image"
+                                    src="/svgs/conference/index/language-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2017.language" /></td>
+                            <td>{'English'}</td>
+                        </tr>
+                    </tbody>
+                </table>
+                <a
+                    className="button mod-2017-panel"
+                    href="http://scratch2017bdx.org"
+                >
+                    <FormattedMessage id="conference-2017.website" />
+                </a>
+            </section>
+            <section className="conf2017-panel mod-hungary">
+                <FlexRow className="conf2017-panel-title">
+                    <img
+                        alt="Hungary Flag"
+                        className="conf2017-panel-flag"
+                        src="/svgs/conference/flags/hu.svg"
+                    />
+                    <div className="conf2017-panel-title-text">
+                        <h3><FormattedMessage id="conference-2017.hungaryTitle" /></h3>
+                    </div>
+                </FlexRow>
+                <p className="conf2017-panel-desc">
+                    <FormattedMessage id="conference-2017.hungaryDesc" />
+                </p>
+                <table className="conf2017-panel-details">
+                    <tbody>
+                        <tr className="conf2017-panel-row">
+                            <td className="conf2017-panel-row-icon">
+                                <img
+                                    alt="Calendar Icon"
+                                    className="conf2017-panel-row-icon-image"
+                                    src="/svgs/conference/index/calendar-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2017.date" /></td>
+                            <td>
+                                <FormattedDate
+                                    day="2-digit"
+                                    month="long"
+                                    value={new Date(2017, 7, 24)}
+                                    year="numeric"
+                                />
+                                {' - '}
+                                <FormattedDate
+                                    day="2-digit"
+                                    month="long"
+                                    value={new Date(2017, 7, 25)}
+                                    year="numeric"
+                                />
+                            </td>
+                        </tr>
+                        <tr className="conf2017-panel-row">
+                            <td className="conf2017-panel-row-icon">
+                                <img
+                                    alt="Map Icon"
+                                    className="conf2017-panel-row-icon-image"
+                                    src="/svgs/conference/index/map-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2017.location" /></td>
+                            <td>{'Budapest, Hungary'}</td>
+                        </tr>
+                        <tr className="conf2017-panel-row">
+                            <td className="conf2017-panel-row-icon">
+                                <img
+                                    alt="Audience Icon"
+                                    className="conf2017-panel-row-icon-image"
+                                    src="/svgs/conference/index/audience-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2017.audience" /></td>
+                            <td><FormattedMessage id="conference-2017.hungaryAudience" /></td>
+                        </tr>
+                        <tr className="conf2017-panel-row">
+                            <td className="conf2017-panel-row-icon">
+                                <img
+                                    alt="Language Icon"
+                                    className="conf2017-panel-row-icon-image"
+                                    src="/svgs/conference/index/language-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2017.language" /></td>
+                            <td>{'English'}</td>
+                        </tr>
+                    </tbody>
+                </table>
+                <a
+                    className="button mod-2017-panel"
+                    href="https://events.epam.com/events/scratch-2017"
+                >
+                    <FormattedMessage id="conference-2017.website" />
+                </a>
+            </section>
+            <section className="conf2017-panel mod-costarica">
+                <FlexRow className="conf2017-panel-title">
+                    <img
+                        alt="Costa Rica Flag"
+                        className="conf2017-panel-flag"
+                        src="/svgs/conference/flags/cr.svg"
+                    />
+                    <div className="conf2017-panel-title-text">
+                        <h3><FormattedMessage id="conference-2017.costaricaTitle" /></h3>
+                        <h4><FormattedMessage id="conference-2017.costaricaSubTitle" /></h4>
+                    </div>
+                </FlexRow>
+                <p className="conf2017-panel-desc">
+                    <FormattedMessage id="conference-2017.costaricaDesc" />
+                </p>
+                <table className="conf2017-panel-details">
+                    <tbody>
+                        <tr className="conf2017-panel-row">
+                            <td className="conf2017-panel-row-icon">
+                                <img
+                                    alt="Calendar Icon"
+                                    className="conf2017-panel-row-icon-image"
+                                    src="/svgs/conference/index/calendar-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2017.date" /></td>
+                            <td>
+                                <FormattedDate
+                                    day="2-digit"
+                                    month="long"
+                                    value={new Date(2017, 10, 12)}
+                                    year="numeric"
+                                />
+                            </td>
+                        </tr>
+                        <tr className="conf2017-panel-row">
+                            <td className="conf2017-panel-row-icon">
+                                <img
+                                    alt="Map Icon"
+                                    className="conf2017-panel-row-icon-image"
+                                    src="/svgs/conference/index/map-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2017.location" /></td>
+                            <td>{'San José, Costa Rica'}</td>
+                        </tr>
+                        <tr className="conf2017-panel-row">
+                            <td className="conf2017-panel-row-icon">
+                                <img
+                                    alt="Audience Icon"
+                                    className="conf2017-panel-row-icon-image"
+                                    src="/svgs/conference/index/audience-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2017.audience" /></td>
+                            <td><FormattedMessage id="conference-2017.costaricaAudience" /></td>
+                        </tr>
+                        <tr className="conf2017-panel-row">
+                            <td className="conf2017-panel-row-icon">
+                                <img
+                                    alt="Language Icon"
+                                    className="conf2017-panel-row-icon-image"
+                                    src="/svgs/conference/index/language-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2017.language" /></td>
+                            <td>{'Español (Spanish)'}</td>
+                        </tr>
+                    </tbody>
+                </table>
+                <a
+                    className="button mod-2017-panel"
+                    href="https://scratchcostarica.com/"
+                >
+                    <FormattedMessage id="conference-2017.website" />
+                </a>
+            </section>
+            <section className="conf2017-panel mod-chile">
+                <FlexRow className="conf2017-panel-title">
+                    <img
+                        alt="Chile Flag"
+                        className="conf2017-panel-flag"
+                        src="/svgs/conference/flags/cl.svg"
+                    />
+                    <div className="conf2017-panel-title-text">
+                        <h3><FormattedMessage id="conference-2017.chileTitle" /></h3>
+                        <h4><FormattedMessage id="conference-2017.chileSubTitle" /></h4>
+                    </div>
+                </FlexRow>
+                <p className="conf2017-panel-desc">
+                    <FormattedMessage id="conference-2017.chileDesc" />
+                </p>
+                <table className="conf2017-panel-details">
+                    <tbody>
+                        <tr className="conf2017-panel-row">
+                            <td className="conf2017-panel-row-icon">
+                                <img
+                                    alt="Calendar Icon"
+                                    className="conf2017-panel-row-icon-image"
+                                    src="/svgs/conference/index/calendar-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2017.date" /></td>
+                            <td>
+                                <FormattedDate
+                                    day="2-digit"
+                                    month="long"
+                                    value={new Date(2017, 7, 31)}
+                                    year="numeric"
+                                />
+                                {' - '}
+                                <FormattedDate
+                                    day="2-digit"
+                                    month="long"
+                                    value={new Date(2017, 8, 1)}
+                                    year="numeric"
+                                />
+                            </td>
+                        </tr>
+                        <tr className="conf2017-panel-row">
+                            <td className="conf2017-panel-row-icon">
+                                <img
+                                    alt="Map Icon"
+                                    className="conf2017-panel-row-icon-image"
+                                    src="/svgs/conference/index/map-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2017.location" /></td>
+                            <td>{'Santiago, Chile'}</td>
+                        </tr>
+                        <tr className="conf2017-panel-row">
+                            <td className="conf2017-panel-row-icon">
+                                <img
+                                    alt="Audience Icon"
+                                    className="conf2017-panel-row-icon-image"
+                                    src="/svgs/conference/index/audience-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2017.audience" /></td>
+                            <td><FormattedMessage id="conference-2017.chileAudience" /></td>
+                        </tr>
+                        <tr className="conf2017-panel-row">
+                            <td className="conf2017-panel-row-icon">
+                                <img
+                                    alt="Language Icon"
+                                    className="conf2017-panel-row-icon-image"
+                                    src="/svgs/conference/index/language-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2017.language" /></td>
+                            <td>{'Español (Spanish) - simultaneous translation during plenary sessions'}</td>
+                        </tr>
+                    </tbody>
+                </table>
+                <a
+                    className="button mod-2017-panel"
+                    href="http://www.scratchalsur.org"
+                >
+                    <FormattedMessage id="conference-2017.website" />
+                </a>
+            </section>
+            <section className="conf2017-panel mod-brasil">
+                <FlexRow className="conf2017-panel-title">
+                    <img
+                        alt="Brasil Flag"
+                        className="conf2017-panel-flag"
+                        src="/svgs/conference/flags/br.svg"
+                    />
+                    <div className="conf2017-panel-title-text">
+                        <h3><FormattedMessage id="conference-2017.brasilTitle" /></h3>
+                    </div>
+                </FlexRow>
+                <p className="conf2017-panel-desc">
+                    <FormattedMessage id="conference-2017.brasilDesc" />
+                </p>
+                <table className="conf2017-panel-details">
+                    <tbody>
+                        <tr className="conf2017-panel-row">
+                            <td className="conf2017-panel-row-icon">
+                                <img
+                                    alt="Calendar Icon"
+                                    className="conf2017-panel-row-icon-image"
+                                    src="/svgs/conference/index/calendar-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2017.date" /></td>
+                            <td>
+                                <FormattedDate
+                                    day="2-digit"
+                                    month="long"
+                                    value={new Date(2017, 9, 5)}
+                                    year="numeric"
+                                />
+                                {' - '}
+                                <FormattedDate
+                                    day="2-digit"
+                                    month="long"
+                                    value={new Date(2017, 9, 7)}
+                                    year="numeric"
+                                />
+                            </td>
+                        </tr>
+                        <tr className="conf2017-panel-row">
+                            <td className="conf2017-panel-row-icon">
+                                <img
+                                    alt="Map Icon"
+                                    className="conf2017-panel-row-icon-image"
+                                    src="/svgs/conference/index/map-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2017.location" /></td>
+                            <td>{'São Paulo, Brasil'}</td>
+                        </tr>
+                        <tr className="conf2017-panel-row">
+                            <td className="conf2017-panel-row-icon">
+                                <img
+                                    alt="Audience Icon"
+                                    className="conf2017-panel-row-icon-image"
+                                    src="/svgs/conference/index/audience-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2017.audience" /></td>
+                            <td><FormattedMessage id="conference-2017.brasilAudience" /></td>
+                        </tr>
+                        <tr className="conf2017-panel-row">
+                            <td className="conf2017-panel-row-icon">
+                                <img
+                                    alt="Language Icon"
+                                    className="conf2017-panel-row-icon-image"
+                                    src="/svgs/conference/index/language-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2017.language" /></td>
+                            <td>{'Português (Portuguese)'}</td>
+                        </tr>
+                    </tbody>
+                </table>
+                <a
+                    className="button mod-2017-panel"
+                    href="http://scratchbrasil.org/"
+                >
+                    <FormattedMessage id="conference-2017.website" />
+                </a>
+            </section>
+            <section className="conf2017-panel mod-china mod-last">
+                <FlexRow className="conf2017-panel-title">
+                    <img
+                        alt="China Flag"
+                        className="conf2017-panel-flag"
+                        src="/svgs/conference/flags/cn.svg"
+                    />
+                    <div className="conf2017-panel-title-text">
+                        <h3><FormattedMessage id="conference-2017.chinaTitle" /></h3>
+                    </div>
+                </FlexRow>
+                <p className="conf2017-panel-desc">
+                    <FormattedMessage id="conference-2017.chinaDesc" />
+                </p>
+                <table className="conf2017-panel-details">
+                    <tbody>
+                        <tr className="conf2017-panel-row">
+                            <td className="conf2017-panel-row-icon">
+                                <img
+                                    alt="Calendar Icon"
+                                    className="conf2017-panel-row-icon-image"
+                                    src="/svgs/conference/index/calendar-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2017.date" /></td>
+                            <td>
+                                <FormattedDate
+                                    day="2-digit"
+                                    month="long"
+                                    value={new Date(2017, 4, 20)}
+                                    year="numeric"
+                                />
+                                {' - '}
+                                <FormattedDate
+                                    day="2-digit"
+                                    month="long"
+                                    value={new Date(2017, 4, 21)}
+                                    year="numeric"
+                                />
+                            </td>
+                        </tr>
+                        <tr className="conf2017-panel-row">
+                            <td className="conf2017-panel-row-icon">
+                                <img
+                                    alt="Map Icon"
+                                    className="conf2017-panel-row-icon-image"
+                                    src="/svgs/conference/index/map-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2017.location" /></td>
+                            <td>{'Shanghai, China'}</td>
+                        </tr>
+                        <tr className="conf2017-panel-row">
+                            <td className="conf2017-panel-row-icon">
+                                <img
+                                    alt="Audience Icon"
+                                    className="conf2017-panel-row-icon-image"
+                                    src="/svgs/conference/index/audience-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2017.audience" /></td>
+                            <td><FormattedMessage id="conference-2017.chinaAudience" /></td>
+                        </tr>
+                        <tr className="conf2017-panel-row">
+                            <td className="conf2017-panel-row-icon">
+                                <img
+                                    alt="Language Icon"
+                                    className="conf2017-panel-row-icon-image"
+                                    src="/svgs/conference/index/language-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2017.language" /></td>
+                            <td>{'中文 (Chinese)'}</td>
+                        </tr>
+                    </tbody>
+                </table>
+                <a
+                    className="button mod-2017-panel"
+                    href="http://scratchconference2017.sxl.cn/"
+                >
+                    <FormattedMessage id="conference-2017.website" />
+                </a>
+            </section>
+        </div>
+    </div>
+);
 
 render(<Page><ConferenceSplash /></Page>, document.getElementById('app'));
diff --git a/src/views/conference/2018/index/index.jsx b/src/views/conference/2018/index/index.jsx
index d4bc98064..00bb4447f 100644
--- a/src/views/conference/2018/index/index.jsx
+++ b/src/views/conference/2018/index/index.jsx
@@ -1,152 +1,151 @@
-var FormattedDate = require('react-intl').FormattedDate;
-var FormattedMessage = require('react-intl').FormattedMessage;
-var React = require('react');
-var render = require('../../../../lib/render.jsx');
+const FormattedDate = require('react-intl').FormattedDate;
+const FormattedMessage = require('react-intl').FormattedMessage;
+const React = require('react');
+const render = require('../../../../lib/render.jsx');
 
-var FlexRow = require('../../../../components/flex-row/flex-row.jsx');
-var Page = require('../../../../components/page/conference/2018/page.jsx');
-var TitleBanner = require('../../../../components/title-banner/title-banner.jsx');
+const FlexRow = require('../../../../components/flex-row/flex-row.jsx');
+const Page = require('../../../../components/page/conference/2018/page.jsx');
+const TitleBanner = require('../../../../components/title-banner/title-banner.jsx');
 
 require('../../../../components/forms/button.scss');
 require('./index.scss');
 
-var ConferenceSplash = React.createClass({
-    type: 'ConferenceSplash',
-
-    render: function () {
-        return (
-            <div className='index mod-2018'>
-                <TitleBanner className='mod-conference mod-2018'>
-                    <div className='title-banner-image mod-2018'></div>
-                    <h1 className='title-banner-h1 mod-2018'>
-                        <center>
-                            <FormattedMessage id='conference-2018.title' />
-                            <br />
-                            <FormattedMessage id='conference-2018.subtitle' />
-                        </center>
-                    </h1>
-                    <h3 className='title-banner-h3 mod-2018'>
-                        <FormattedMessage id='conference-2018.dateDesc' />
-                    </h3>
-                </TitleBanner>
-                <div className='inner'>
-                    <section className='conf2018-panel mod-desc'>
-                        <p className='conf2018-panel-desc'>
-                            <FormattedMessage id='conference-2018.desc1' />
-                            <br />
-                            <br />
-                            <FormattedMessage id='conference-2018.desc2' />
-                        </p>
-                        <table className='conf2018-panel-details'>
-                            <tbody>
-                                <tr className='conf2018-panel-row'>
-                                    <td className='conf2018-panel-row-icon'>
-                                        <img
-                                            className='conf2018-panel-row-icon-image'
-                                            src='/svgs/conference/index/calendar-icon.svg'
-                                            alt='Calendar Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2018.date' /></td>
-                                    <td>
-                                        <FormattedDate
-                                            value={new Date(2018, 6, 26)}
-                                            year='numeric'
-                                            month='long'
-                                            day='2-digit'
-                                        />
-                                        {' - '}
-                                        <FormattedDate
-                                            value={new Date(2018, 6, 28)}
-                                            year='numeric'
-                                            month='long'
-                                            day='2-digit'
-                                        />
-                                        <FormattedMessage id='conference-2018.dateDescMore' />
-                                    </td>
-                                </tr>
-                                <tr className='conf2018-panel-row'>
-                                    <td className='conf2018-panel-row-icon'>
-                                        <img
-                                            className='conf2018-panel-row-icon-image'
-                                            src='/svgs/conference/index/map-icon.svg'
-                                            alt='Map Icon'
-                                        />
-                                    </td>
-                                    <td><FormattedMessage id='conference-2018.location' /></td>
-                                    <td><FormattedMessage id='conference-2018.locationDetails' /></td>
-                                </tr>
-                            </tbody>
-                        </table>
-                        <p>
-                            <FormattedMessage id='conference-2018.registrationDate' />
-                        </p>
-                    </section>
-                    <section className='conf2018-panel'>
-                        <p className='conf2018-panel-desc'>
-                            <FormattedMessage id='conference-2018.sessionDesc' />
-                        </p>
-                        <p className='conf2018-panel-session'>
-                            <p className='conf2018-panel-session'>
-                                <b>
-                                    <FormattedMessage id='conference-2018.sessionItem1Title' />
-                                </b>{' '}
-                                <FormattedMessage id='conference-2018.sessionItem1Desc' />
-                            </p>
-                            <p className='conf2018-panel-session'>
-                                <b>
-                                    <FormattedMessage id='conference-2018.sessionItem2Title' />
-                                </b>{' '}
-                                <FormattedMessage id='conference-2018.sessionItem2Desc' />
-                            </p>
-                            <p className='conf2018-panel-session'>
-                                <b>
-                                    <FormattedMessage id='conference-2018.sessionItem3Title' />
-                                </b>{' '}
-                                <FormattedMessage id='conference-2018.sessionItem3Desc' />
-                            </p>
-                            <p className='conf2018-panel-session'>
-                                <b>
-                                    <FormattedMessage id='conference-2018.sessionItem4Title' />
-                                </b>{' '}
-                                <FormattedMessage id='conference-2018.sessionItem4Desc' />
-                            </p>
-                            <p className='conf2018-panel-deadline'>
-                              <FormattedMessage id='conference-2018.deadline' />
-                            </p>
-                        </p>
-                        <a className='button mod-2018-panel' href='https://docs.google.com/forms/d/e/1FAIpQLSd7SkuQ-dfW-P3aArSQokK9GkKAUKufTVBHod_ElNIiFE9iBQ/viewform?usp=sf_link'>
-                            <FormattedMessage id='conference-2018.proposal' />
-                        </a>
-                    </section>
-                    <section className='conf2018-panel mod-registration'>
-                        <FlexRow className='conf2018-panel-title'>
-                            <div className='conf2018-panel-title-text'>
-                                <h3><FormattedMessage id='conference-2018.registrationTitle' /></h3>
-                            </div>
-                        </FlexRow>
-                        <p className='conf2018-panel-desc'>
-                            <FormattedMessage id='conference-2018.registrationEarly' />
-                            <br/>
-                            <FormattedMessage id='conference-2018.registrationStandard' />
-                        </p>
-                    </section>
-                    <section className='conf2018-panel mod-questions'>
-                        <p className='conf2018-panel-desc'>
-                            <FormattedMessage
-                                id='conference-2018.questions'
-                                values={{
-                                    emailLink: <a href='mailto:conference@scratch.mit.edu'>
-                                        conference@scratch.mit.edu
-                                    </a>
-                                }}
-                            />
-                        </p>
-                    </section>
-                </div>
-            </div>
-        );
-    }
-});
+const ConferenceSplash = () => (
+    <div className="index mod-2018">
+        <TitleBanner className="mod-conference mod-2018">
+            <div className="title-banner-image mod-2018" />
+            <h1 className="title-banner-h1 mod-2018">
+                <center>
+                    <FormattedMessage id="conference-2018.title" />
+                    <br />
+                    <FormattedMessage id="conference-2018.subtitle" />
+                </center>
+            </h1>
+            <h3 className="title-banner-h3 mod-2018">
+                <FormattedMessage id="conference-2018.dateDesc" />
+            </h3>
+        </TitleBanner>
+        <div className="inner">
+            <section className="conf2018-panel mod-desc">
+                <p className="conf2018-panel-desc">
+                    <FormattedMessage id="conference-2018.desc1" />
+                    <br />
+                    <br />
+                    <FormattedMessage id="conference-2018.desc2" />
+                </p>
+                <table className="conf2018-panel-details">
+                    <tbody>
+                        <tr className="conf2018-panel-row">
+                            <td className="conf2018-panel-row-icon">
+                                <img
+                                    alt="Calendar Icon"
+                                    className="conf2018-panel-row-icon-image"
+                                    src="/svgs/conference/index/calendar-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2018.date" /></td>
+                            <td>
+                                <FormattedDate
+                                    day="2-digit"
+                                    month="long"
+                                    value={new Date(2018, 6, 26)}
+                                    year="numeric"
+                                />
+                                {' - '}
+                                <FormattedDate
+                                    day="2-digit"
+                                    month="long"
+                                    value={new Date(2018, 6, 28)}
+                                    year="numeric"
+                                />
+                                <FormattedMessage id="conference-2018.dateDescMore" />
+                            </td>
+                        </tr>
+                        <tr className="conf2018-panel-row">
+                            <td className="conf2018-panel-row-icon">
+                                <img
+                                    alt="Map Icon"
+                                    className="conf2018-panel-row-icon-image"
+                                    src="/svgs/conference/index/map-icon.svg"
+                                />
+                            </td>
+                            <td><FormattedMessage id="conference-2018.location" /></td>
+                            <td><FormattedMessage id="conference-2018.locationDetails" /></td>
+                        </tr>
+                    </tbody>
+                </table>
+                <p>
+                    <FormattedMessage id="conference-2018.registrationDate" />
+                </p>
+            </section>
+            <section className="conf2018-panel">
+                <p className="conf2018-panel-desc">
+                    <FormattedMessage id="conference-2018.sessionDesc" />
+                </p>
+                <p className="conf2018-panel-session">
+                    <p className="conf2018-panel-session">
+                        <b>
+                            <FormattedMessage id="conference-2018.sessionItem1Title" />
+                        </b>{' '}
+                        <FormattedMessage id="conference-2018.sessionItem1Desc" />
+                    </p>
+                    <p className="conf2018-panel-session">
+                        <b>
+                            <FormattedMessage id="conference-2018.sessionItem2Title" />
+                        </b>{' '}
+                        <FormattedMessage id="conference-2018.sessionItem2Desc" />
+                    </p>
+                    <p className="conf2018-panel-session">
+                        <b>
+                            <FormattedMessage id="conference-2018.sessionItem3Title" />
+                        </b>{' '}
+                        <FormattedMessage id="conference-2018.sessionItem3Desc" />
+                    </p>
+                    <p className="conf2018-panel-session">
+                        <b>
+                            <FormattedMessage id="conference-2018.sessionItem4Title" />
+                        </b>{' '}
+                        <FormattedMessage id="conference-2018.sessionItem4Desc" />
+                    </p>
+                    <p className="conf2018-panel-deadline">
+                        <FormattedMessage id="conference-2018.deadline" />
+                    </p>
+                </p>
+                <a
+                    className="button mod-2018-panel"
+                    href="https://docs.google.com/forms/d/e/1FAIpQLSd7SkuQ-dfW-P3aArSQokK9GkKAUKufTVBHod_ElNIiFE9iBQ/viewform?usp=sf_link"
+                >
+                    <FormattedMessage id="conference-2018.proposal" />
+                </a>
+            </section>
+            <section className="conf2018-panel mod-registration">
+                <FlexRow className="conf2018-panel-title">
+                    <div className="conf2018-panel-title-text">
+                        <h3><FormattedMessage id="conference-2018.registrationTitle" /></h3>
+                    </div>
+                </FlexRow>
+                <p className="conf2018-panel-desc">
+                    <FormattedMessage id="conference-2018.registrationEarly" />
+                    <br />
+                    <FormattedMessage id="conference-2018.registrationStandard" />
+                </p>
+            </section>
+            <section className="conf2018-panel mod-questions">
+                <p className="conf2018-panel-desc">
+                    <FormattedMessage
+                        id="conference-2018.questions"
+                        values={{
+                            emailLink: (
+                                <a href="mailto:conference@scratch.mit.edu">
+                                    conference@scratch.mit.edu
+                                </a>
+                            )
+                        }}
+                    />
+                </p>
+            </section>
+        </div>
+    </div>
+);
 
 render(<Page><ConferenceSplash /></Page>, document.getElementById('app'));
diff --git a/src/views/credits/credits.jsx b/src/views/credits/credits.jsx
index 7006c8470..727d60c91 100644
--- a/src/views/credits/credits.jsx
+++ b/src/views/credits/credits.jsx
@@ -1,320 +1,433 @@
-var React = require('react');
-var render = require('../../lib/render.jsx');
-var FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
-var FormattedMessage = require('react-intl').FormattedMessage;
+const React = require('react');
+const render = require('../../lib/render.jsx');
+const FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
+const FormattedMessage = require('react-intl').FormattedMessage;
 
-var Page = require('../../components/page/www/page.jsx');
+const Page = require('../../components/page/www/page.jsx');
 
 require('./credits.scss');
 
-var Credits = React.createClass({
-    type: 'Credits',
-    render: function () {
-        return (
-            <div className="inner credits">
-                <h1><FormattedMessage id='credits.title' /></h1>
-                <h2>MIT Scratch Team</h2>
-                <p><FormattedMessage id='credits.developers' /></p>
+const Credits = () => (
+    <div className="inner credits">
+        <h1><FormattedMessage id="credits.title" /></h1>
+        <h2>MIT Scratch Team</h2>
+        <p><FormattedMessage id="credits.developers" /></p>
 
-                <ul>
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/2755634_170x170.png" alt="Christan Avatar" />
-                        <span className="name">Christan Balch</span>
-                    </li>
+        <ul>
+            <li>
+                <img
+                    alt="Christan Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/2755634_170x170.png"
+                />
+                <span className="name">Christan Balch</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/3581881_170x170.png" alt="Carl Avatar" />
-                        <span className="name">Carl Bowman</span>
-                    </li>
-                                        
-                    <li>
-                        <img src="//cdn2.scratch.mit.edu/get_image/user/27383273_60x60.png" alt="Karishma Avatar" />
-                        <span className="name">Karishma Chadha</span>
-                    </li>
+            <li>
+                <img
+                    alt="Carl Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/3581881_170x170.png"
+                />
+                <span className="name">Carl Bowman</span>
+            </li>
+                                
+            <li>
+                <img
+                    alt="Karishma Avatar"
+                    src="//cdn2.scratch.mit.edu/get_image/user/27383273_60x60.png"
+                />
+                <span className="name">Karishma Chadha</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/900283_170x170.png" alt="Champika Avatar" />
-                        <span className="name">Champika Fernando</span>
-                    </li>
+            <li>
+                <img
+                    alt="Champika Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/900283_170x170.png"
+                />
+                <span className="name">Champika Fernando</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/24137617_170x170.png" alt="Mark Avatar" />
-                        <span className="name">Mark Ferrell</span>
-                    </li>
+            <li>
+                <img
+                    alt="Mark Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/24137617_170x170.png"
+                />
+                <span className="name">Mark Ferrell</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/1494_170x170.png" alt="Chris Avatar" />
-                        <span className="name">Chris Garrity</span>
-                    </li>
+            <li>
+                <img
+                    alt="Chris Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/1494_170x170.png"
+                />
+                <span className="name">Chris Garrity</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/10866958_170x170.png" alt="Colby Avatar" />
-                        <span className="name">Colby Gutierrez-Kraybill</span>
-                    </li>
+            <li>
+                <img
+                    alt="Colby Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/10866958_170x170.png"
+                />
+                <span className="name">Colby Gutierrez-Kraybill</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/21986973_170x170.png" alt="Paul Avatar" />
-                        <span className="name">Paul Kaplan</span>
-                    </li>
+            <li>
+                <img
+                    alt="Paul Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/21986973_170x170.png"
+                />
+                <span className="name">Paul Kaplan</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/527836_170x170.png" alt="DD Avatar" />
-                        <span className="name">DD Liu</span>
-                    </li>
+            <li>
+                <img
+                    alt="DD Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/527836_170x170.png"
+                />
+                <span className="name">DD Liu</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/3714374_170x170.png" alt="Shruti Avatar" />
-                        <span className="name">Shruti Mohnot</span>
-                    </li>
+            <li>
+                <img
+                    alt="Shruti Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/3714374_170x170.png"
+                />
+                <span className="name">Shruti Mohnot</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/246290_170x170.png" alt="Sarah Avatar" />
-                        <span className="name">Sarah Otts</span>
-                    </li>
+            <li>
+                <img
+                    alt="Sarah Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/246290_170x170.png"
+                />
+                <span className="name">Sarah Otts</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/2286560_170x170.png" alt="Carmelo Avatar" />
-                        <span className="name">Carmelo Presicce</span>
-                    </li>
+            <li>
+                <img
+                    alt="Carmelo Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/2286560_170x170.png"
+                />
+                <span className="name">Carmelo Presicce</span>
+            </li>
 
-                    <li>
-                         <img src="//cdn2.scratch.mit.edu/get_image/user/25500116_170x170.png" alt="Tina Avatar" />
-                         <span className="name">Tina Quach</span>
-                    </li>
+            <li>
+                <img
+                    alt="Tina Avatar"
+                    src="//cdn2.scratch.mit.edu/get_image/user/25500116_170x170.png"
+                />
+                <span className="name">Tina Quach</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/167_170x170.png" alt="Mitchel Avatar" />
-                        <span className="name">Mitchel Resnick</span>
-                    </li>
+            <li>
+                <img
+                    alt="Mitchel Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/167_170x170.png"
+                />
+                <span className="name">Mitchel Resnick</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/159_170x170.png" alt="ericr Avatar" />
-                        <span className="name">Eric Rosenbaum</span>
-                    </li>
+            <li>
+                <img
+                    alt="ericr Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/159_170x170.png"
+                />
+                <span className="name">Eric Rosenbaum</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/169_170x170.png" alt="Natalie Avatar" />
-                        <span className="name">Natalie Rusk</span>
-                    </li>
+            <li>
+                <img
+                    alt="Natalie Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/169_170x170.png"
+                />
+                <span className="name">Natalie Rusk</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/2584924_170x170.png" alt="Ray Avatar" />
-                        <span className="name">Ray Schamp</span>
-                    </li>
+            <li>
+                <img
+                    alt="Ray Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/2584924_170x170.png"
+                />
+                <span className="name">Ray Schamp</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/3484484_170x170.png" alt="Eric Avatar" />
-                        <span className="name">Eric Schilling</span>
-                    </li>
+            <li>
+                <img
+                    alt="Eric Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/3484484_170x170.png"
+                />
+                <span className="name">Eric Schilling</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/1709047_170x170.png" alt="Andrew Avatar" />
-                        <span className="name">Andrew Sliwinski</span>
-                    </li>
+            <li>
+                <img
+                    alt="Andrew Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/1709047_170x170.png"
+                />
+                <span className="name">Andrew Sliwinski</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/18417774_170x170.png" alt="Tracy Avatar" />
-                        <span className="name">Tracy Tang</span>
-                    </li>
+            <li>
+                <img
+                    alt="Tracy Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/18417774_170x170.png"
+                />
+                <span className="name">Tracy Tang</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/4373707_170x170.png" alt="Matthew Avatar" />
-                        <span className="name">Matthew Taylor</span>
-                    </li>
+            <li>
+                <img
+                    alt="Matthew Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/4373707_170x170.png"
+                />
+                <span className="name">Matthew Taylor</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/2678986_170x170.png" alt="Moran Avatar" />
-                        <span className="name">Moran Tsur</span>
-                    </li>
+            <li>
+                <img
+                    alt="Moran Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/2678986_170x170.png"
+                />
+                <span className="name">Moran Tsur</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/3532363_170x170.png" alt="Chris Avatar" />
-                        <span className="name">Chris Willis-Ford</span>
-                    </li>
+            <li>
+                <img
+                    alt="Chris Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/3532363_170x170.png"
+                />
+                <span className="name">Chris Willis-Ford</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/2796185_170x170.png" alt="Julia Avatar" />
-                        <span className="name">Julia Zimmerman</span>
-                    </li>
-                </ul>
+            <li>
+                <img
+                    alt="Julia Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/2796185_170x170.png"
+                />
+                <span className="name">Julia Zimmerman</span>
+            </li>
+        </ul>
 
-                <p><FormattedMessage id='credits.moderators' /></p>
+        <p><FormattedMessage id="credits.moderators" /></p>
 
-                <ul>
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/2496866_170x170.png" alt="Jolie Avatar" />
-                        <span className="name">Jolie Castellucci</span>
-                    </li>
+        <ul>
+            <li>
+                <img
+                    alt="Jolie Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/2496866_170x170.png"
+                />
+                <span className="name">Jolie Castellucci</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/24164779_170x170.png" alt="Ellen Avatar" />
-                        <span className="name">Ellen Daoust</span>
-                    </li>
+            <li>
+                <img
+                    alt="Ellen Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/24164779_170x170.png"
+                />
+                <span className="name">Ellen Daoust</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/1048810_170x170.png" alt="Linda Avatar" />
-                        <span className="name">Linda Fernsel</span>
-                    </li>
+            <li>
+                <img
+                    alt="Linda Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/1048810_170x170.png"
+                />
+                <span className="name">Linda Fernsel</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/49156_170x170.png" alt="Mark Avatar" />
-                        <span className="name">Mark Goff</span>
-                    </li>
+            <li>
+                <img
+                    alt="Mark Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/49156_170x170.png"
+                />
+                <span className="name">Mark Goff</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn2.scratch.mit.edu/get_image/user/26249744_60x60.png" alt="Joel Avatar" />
-                        <span className="name">Joel Gritter</span>
-                    </li>
+            <li>
+                <img
+                    alt="Joel Avatar"
+                    src="//cdn2.scratch.mit.edu/get_image/user/26249744_60x60.png"
+                />
+                <span className="name">Joel Gritter</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn2.scratch.mit.edu/get_image/user/5311910_60x60.png" alt="Carolina Avatar" />
-                        <span className="name">Carolina Kaufman</span>
-                    </li>
+            <li>
+                <img
+                    alt="Carolina Avatar"
+                    src="//cdn2.scratch.mit.edu/get_image/user/5311910_60x60.png"
+                />
+                <span className="name">Carolina Kaufman</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/373646_170x170.png" alt="Dalton Avatar" />
-                        <span className="name">Dalton Miner</span>
-                    </li>
+            <li>
+                <img
+                    alt="Dalton Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/373646_170x170.png"
+                />
+                <span className="name">Dalton Miner</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/159139_170x170.png" alt="Franchette Avatar" />
-                        <span className="name">Franchette Viloria</span>
-                    </li>
+            <li>
+                <img
+                    alt="Franchette Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/159139_170x170.png"
+                />
+                <span className="name">Franchette Viloria</span>
+            </li>
 
-                    <li>
-                        <img src="//cdn.scratch.mit.edu/get_image/user/4747093_170x170.png" alt="Annie Avatar" />
-                        <span className="name">Annie Whitehouse</span>
-                    </li>
-                </ul>
+            <li>
+                <img
+                    alt="Annie Avatar"
+                    src="//cdn.scratch.mit.edu/get_image/user/4747093_170x170.png"
+                />
+                <span className="name">Annie Whitehouse</span>
+            </li>
+        </ul>
 
-                <h2><FormattedMessage id='credits.previousTitle' /></h2>
-                <p><FormattedMessage id='credits.previousBody' />
-                &nbsp;
-                Ben Berg,
-                Amos Blanton,
-                Karen Brennan,
-                Juanita Buitrago,
-                Leo Burd,
-                Gaia Carini,
-                Kasia Chmielinski,
-                Michelle Chung,
-                Shane Clements,
-                Hannah Cole,
-                Sayamindu Dasgupta,
-                Margarita Dekoli,
-                Evelyn Eastmond,
-                Dave Feinberg,
-                Chris Graves,
-                Megan Haddadi,
-                Connor Hudson,
-                Christina Huang,
-                Tony Hwang,
-                Abdulrahman Idlbi,
-                Randy Jou,
-                Lily Kim,
-                Tauntaun Kim,
-                Saskia Leggett,
-                Tim Mickel,
-                Amon Millner,
-                Ricarose Roque,
-                Andrea Saxman,
-                Jay Silver,
-                Tammy Stern,
-                Lis Sylvan,
-                Hanako Tjia,
-                Claudia Urrea,
-                Oren Zuckerman
-                </p>
+        <h2><FormattedMessage id="credits.previousTitle" /></h2>
+        <p>
+            <FormattedMessage id="credits.previousBody" />
+            &nbsp;
+            Ben Berg,
+            Amos Blanton,
+            Karen Brennan,
+            Juanita Buitrago,
+            Leo Burd,
+            Gaia Carini,
+            Kasia Chmielinski,
+            Michelle Chung,
+            Shane Clements,
+            Hannah Cole,
+            Sayamindu Dasgupta,
+            Margarita Dekoli,
+            Evelyn Eastmond,
+            Dave Feinberg,
+            Chris Graves,
+            Megan Haddadi,
+            Connor Hudson,
+            Christina Huang,
+            Tony Hwang,
+            Abdulrahman Idlbi,
+            Randy Jou,
+            Lily Kim,
+            Tauntaun Kim,
+            Saskia Leggett,
+            Tim Mickel,
+            Amon Millner,
+            Ricarose Roque,
+            Andrea Saxman,
+            Jay Silver,
+            Tammy Stern,
+            Lis Sylvan,
+            Hanako Tjia,
+            Claudia Urrea,
+            Oren Zuckerman
+        </p>
 
-                <h2><FormattedMessage id='credits.partnersTitle' /></h2>
-                <p><FormattedMessage id='credits.partnersBody' /></p>
+        <h2>
+            <FormattedMessage id="credits.partnersTitle" />
+        </h2>
+        <p>
+            <FormattedMessage id="credits.partnersBody" />
+        </p>
 
-                <h2><FormattedMessage id='credits.researchersTitle' /></h2>
-                <p><FormattedHTMLMessage id='credits.researchersBody' /></p>
+        <h2>
+            <FormattedMessage id="credits.researchersTitle" />
+        </h2>
+        <p>
+            <FormattedHTMLMessage id="credits.researchersBody" />
+        </p>
 
-                <h2><FormattedMessage id='credits.acknowledgementsTitle' /></h2>
-                <p>
-                  <FormattedHTMLMessage id='credits.acknowledgementsContributors' />
-                    &nbsp;
-                    Susan Abend, Robbie Berg, Lauren Bessen, Keith Braadfladt, Susan Carillo,
-                    Will Denton, Nathan Dinsmore, Catherine Feldman, Jodi Finch, Ioana Fineberg,
-                    JT Galla, Rachel Garber, Chris Garrity, Cassy Gibbs, Brian Harvey,
-                    Roland Hebert, Tracy Ho, Benjamin Howe, Kapaya Katongo, Evan Karatzas,
-                    Christine Kim, Joren Lauwers, Mike Lee, Jeff Lieberman, Mark Loughridge,
-                    Kelly Liu, Anthony Lu, Danny Lutz, David Malan, Wayne Marshall,
-                    John McIntosh, Paul Medlock-Walton, Dongfang (Tian) Mi, Ximena Miranda,
-                    Jens Moenig, Evan Moore, Geetha Narayanan, Kate Nazemi, Liddy Nevile,
-                    Wing Ngan, Derek O'Connell, Tim Radvan, Karen Randall, Ian Reynolds,
-                    Miriam Ruiz, Chinua Shaw, Ed Shems, Cynthia Solomon, Daniel Strimpel,
-                    Kilmer Sweazy, John Henry Thompson, Ubong Ukoh, Vladimir Vuksan, Han Xu.
-                    &nbsp;
-                  <FormattedHTMLMessage id='credits.acknowledgementsTranslators' />
-                </p>
-                <p><FormattedMessage id='credits.acknowledgementsCommunity' /></p>
-                <p><FormattedMessage id='credits.acknowledgementsInfluencers' /></p>
-                <h2><FormattedMessage id='credits.supportersTitle' /></h2>
-                <p><FormattedMessage id='credits.supportersFinancialHeader' /></p>
-                <p>
-                    <a href="http://www.nsf.gov/">National Science Foundation</a>,
-                    <a href="http://www.scratchfoundation.org/"> Scratch Foundation</a>,
-                    <a href="http://www.google.org/"> Google</a>,
-                    <a href="http://www.legofoundation.com/"> LEGO Foundation</a>,
-                    <a href="http://www.intel.com/"> Intel</a>,
-                    <a href="http://www.turner.com/company/"> Cartoon Network</a>,
-                    <a href="http://www.fundacaolemann.org.br/lemann-foundation/"> Lemann Foundation</a>,
-                    <a href="https://www.macfound.org/"> MacArthur Foundation</a>.
-                </p>
+        <h2>
+            <FormattedMessage id="credits.acknowledgementsTitle" />
+        </h2>
+        <p>
+            <FormattedHTMLMessage id="credits.acknowledgementsContributors" />
+            &nbsp;
+            Susan Abend, Robbie Berg, Lauren Bessen, Keith Braadfladt, Susan Carillo,
+            Will Denton, Nathan Dinsmore, Catherine Feldman, Jodi Finch, Ioana Fineberg,
+            JT Galla, Rachel Garber, Chris Garrity, Cassy Gibbs, Brian Harvey,
+            Roland Hebert, Tracy Ho, Benjamin Howe, Kapaya Katongo, Evan Karatzas,
+            Christine Kim, Joren Lauwers, Mike Lee, Jeff Lieberman, Mark Loughridge,
+            Kelly Liu, Anthony Lu, Danny Lutz, David Malan, Wayne Marshall,
+            John McIntosh, Paul Medlock-Walton, Dongfang (Tian) Mi, Ximena Miranda,
+            Jens Moenig, Evan Moore, Geetha Narayanan, Kate Nazemi, Liddy Nevile,
+            Wing Ngan, Derek O&#39;Connell, Tim Radvan, Karen Randall, Ian Reynolds,
+            Miriam Ruiz, Chinua Shaw, Ed Shems, Cynthia Solomon, Daniel Strimpel,
+            Kilmer Sweazy, John Henry Thompson, Ubong Ukoh, Vladimir Vuksan, Han Xu.
+            &nbsp;
+            <FormattedHTMLMessage id="credits.acknowledgementsTranslators" />
+        </p>
+        <p>
+            <FormattedMessage id="credits.acknowledgementsCommunity" />
+        </p>
+        <p>
+            <FormattedMessage id="credits.acknowledgementsInfluencers" />
+        </p>
+        <h2>
+            <FormattedMessage id="credits.supportersTitle" />
+        </h2>
+        <p>
+            <FormattedMessage id="credits.supportersFinancialHeader" />
+        </p>
+        <p>
+            <a href="http://www.nsf.gov/">National Science Foundation</a>,
+            <a href="http://www.scratchfoundation.org/"> Scratch Foundation</a>,
+            <a href="http://www.google.org/"> Google</a>,
+            <a href="http://www.legofoundation.com/"> LEGO Foundation</a>,
+            <a href="http://www.intel.com/"> Intel</a>,
+            <a href="http://www.turner.com/company/"> Cartoon Network</a>,
+            <a href="http://www.fundacaolemann.org.br/lemann-foundation/"> Lemann Foundation</a>,
+            <a href="https://www.macfound.org/"> MacArthur Foundation</a>.
+        </p>
 
-                <p><FormattedMessage id='credits.supportersServicesHeader' /></p>
-                <p>
-                    <a href="http://www.advancedinstaller.com/"> Advanced Installer</a>,
-                    <a href="http://aws.amazon.com/"> Amazon Web Services</a>,
-                    <a href="https://codetree.com/"> Codetree</a>,
-                    <a href="https://www.fastly.com/"> Fastly</a>,
-                    <a href="https://www.geckoboard.com"> Geckoboard</a>,
-                    <a href="https://github.com/"> Github</a>,
-                    <a href="https://www.inversoft.com/"> Inversoft</a>,
-                    <a href="http://mailchimp.com/"> MailChimp</a>,
-                    <a href="http://mandrill.com/">  Mandrill</a>,
-                    <a href="http://newrelic.com/"> New Relic</a>,
-                    <a href="https://www.pagerduty.com/"> PagerDuty</a>,
-                    <a href="https://www.pingdom.com/"> Pingdom</a>,
-                    <a href="https://www.rallydev.com/"> Rally</a>,
-                    <a href="https://saucelabs.com/"> SauceLabs</a>,
-                    <a href="https://screenhero.com/"> Screenhero</a>,
-                    <a href="https://getsentry.com/welcome/"> Sentry</a>,
-                    <a href="http://www.git-tower.com/"> Tower</a>,
-                    <a href="https://www.transifex.com/"> Transifex</a>,
-                    <a href="https://travis-ci.org/"> Travis-CI</a>.
-                </p>
+        <p><FormattedMessage id="credits.supportersServicesHeader" /></p>
+        <p>
+            <a href="http://www.advancedinstaller.com/"> Advanced Installer</a>,
+            <a href="http://aws.amazon.com/"> Amazon Web Services</a>,
+            <a href="https://codetree.com/"> Codetree</a>,
+            <a href="https://www.fastly.com/"> Fastly</a>,
+            <a href="https://www.geckoboard.com"> Geckoboard</a>,
+            <a href="https://github.com/"> Github</a>,
+            <a href="https://www.inversoft.com/"> Inversoft</a>,
+            <a href="http://mailchimp.com/"> MailChimp</a>,
+            <a href="http://mandrill.com/">  Mandrill</a>,
+            <a href="http://newrelic.com/"> New Relic</a>,
+            <a href="https://www.pagerduty.com/"> PagerDuty</a>,
+            <a href="https://www.pingdom.com/"> Pingdom</a>,
+            <a href="https://www.rallydev.com/"> Rally</a>,
+            <a href="https://saucelabs.com/"> SauceLabs</a>,
+            <a href="https://screenhero.com/"> Screenhero</a>,
+            <a href="https://getsentry.com/welcome/"> Sentry</a>,
+            <a href="http://www.git-tower.com/"> Tower</a>,
+            <a href="https://www.transifex.com/"> Transifex</a>,
+            <a href="https://travis-ci.org/"> Travis-CI</a>.
+        </p>
 
-                <p><FormattedMessage id='credits.supportersOpenHeader' /></p>
-                <p>
-                    <a href="https://www.djangoproject.com/"> Django</a>,
-                    <a href="http://djangobb.org/"> DjangoBB</a>,
-                    <a href="https://www.docker.com/"> Docker</a>,
-                    <a href="https://www.elastic.co/"> Elasticsearch</a>,
-                    <a href="http://ganglia.sourceforge.net/"> Ganglia</a>,
-                    <a href="http://gunicorn.org"> Gunicorn</a>,
-                    <a href="https://jenkins-ci.org/"> Jenkins</a>,
-                    <a href="http://www.linux.org/"> Linux</a>,
-                    <a href="http://memcached.org/"> Memcached</a>,
-                    <a href="https://www.mediawiki.org/wiki/MediaWiki"> MediaWiki</a>,
-                    <a href="http://www.mysql.com/"> MySQL</a>,
-                    <a href="https://www.nagios.org/"> Nagios</a>,
-                    <a href="https://www.nginx.com/resources/wiki/"> Nginx</a>,
-                    <a href="https://nodejs.org/en/"> Node.js</a>,
-                    <a href="http://www.postgresql.org/"> PostgreSQL</a>,
-                    <a href="https://www.python.org/"> Python</a>,
-                    <a href="http://redis.io/"> Redis</a>,
-                    <a href="http://saltstack.com/"> SaltStack</a>,
-                    <a href="https://github.com/etsy/statsd/"> StatsD</a>,
-                    <a href="http://www.ubuntu.com/"> Ubuntu</a>,
-                    <a href="https://www.varnish-cache.org/"> Varnish</a>.
-                </p>
-            </div>
-        );
-    }
-});
+        <p><FormattedMessage id="credits.supportersOpenHeader" /></p>
+        <p>
+            <a href="https://www.djangoproject.com/"> Django</a>,
+            <a href="http://djangobb.org/"> DjangoBB</a>,
+            <a href="https://www.docker.com/"> Docker</a>,
+            <a href="https://www.elastic.co/"> Elasticsearch</a>,
+            <a href="http://ganglia.sourceforge.net/"> Ganglia</a>,
+            <a href="http://gunicorn.org"> Gunicorn</a>,
+            <a href="https://jenkins-ci.org/"> Jenkins</a>,
+            <a href="http://www.linux.org/"> Linux</a>,
+            <a href="http://memcached.org/"> Memcached</a>,
+            <a href="https://www.mediawiki.org/wiki/MediaWiki"> MediaWiki</a>,
+            <a href="http://www.mysql.com/"> MySQL</a>,
+            <a href="https://www.nagios.org/"> Nagios</a>,
+            <a href="https://www.nginx.com/resources/wiki/"> Nginx</a>,
+            <a href="https://nodejs.org/en/"> Node.js</a>,
+            <a href="http://www.postgresql.org/"> PostgreSQL</a>,
+            <a href="https://www.python.org/"> Python</a>,
+            <a href="http://redis.io/"> Redis</a>,
+            <a href="http://saltstack.com/"> SaltStack</a>,
+            <a href="https://github.com/etsy/statsd/"> StatsD</a>,
+            <a href="http://www.ubuntu.com/"> Ubuntu</a>,
+            <a href="https://www.varnish-cache.org/"> Varnish</a>.
+        </p>
+    </div>
+);
 
 render(<Page><Credits /></Page>, document.getElementById('app'));
diff --git a/src/views/developers/developers.jsx b/src/views/developers/developers.jsx
index 7c9035a59..f263d4bb5 100644
--- a/src/views/developers/developers.jsx
+++ b/src/views/developers/developers.jsx
@@ -1,248 +1,267 @@
-var React = require('react');
-var render = require('../../lib/render.jsx');
+const FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
+const FormattedMessage = require('react-intl').FormattedMessage;
+const React = require('react');
 
-var FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
-var FormattedMessage = require('react-intl').FormattedMessage;
+const FlexRow = require('../../components/flex-row/flex-row.jsx');
+const SubNavigation = require('../../components/subnavigation/subnavigation.jsx');
+const TitleBanner = require('../../components/title-banner/title-banner.jsx');
 
-var Page = require('../../components/page/www/page.jsx');
-var FlexRow = require('../../components/flex-row/flex-row.jsx');
-var SubNavigation = require('../../components/subnavigation/subnavigation.jsx');
-var TitleBanner = require('../../components/title-banner/title-banner.jsx');
+const render = require('../../lib/render.jsx');
+const Page = require('../../components/page/www/page.jsx');
 
 require('./developers.scss');
 
-var Developers = React.createClass({
-    type: 'Developers',
-    render: function () {
-        return (
-            <div className="developers">
-                <TitleBanner className="masthead">
-                    <div className="inner">
-                        <h1 className="title-banner-h1">
-                            <FormattedMessage id='developers.title' />
-                        </h1>
-                        <p className="title-banner-p intro">
-                            <FormattedHTMLMessage id='developers.intro' />
-                        </p>
-                    </div>
-                    <div className="band">
-                        <SubNavigation className="inner">
-                            <a href="#projects">
-                                <li>
-                                    <FormattedMessage id='developers.projectsTitle' />
-                                </li>
-                            </a>
-                            <a href="#principles">
-                                <li>
-                                    <FormattedMessage id='developers.principlesTitle' />
-                                </li>
-                            </a>
-                            <a href="#join">
-                                <li>
-                                    <FormattedMessage id='developers.joinTitle' />
-                                </li>
-                            </a>
-                            <a href="#donate">
-                                <li>
-                                    <FormattedMessage id='developers.donateTitle' />
-                                </li>
-                            </a>
-                            <a href="#partners">
-                                <li>
-                                    <FormattedMessage id='developers.partnersTitle' />
-                                </li>
-                            </a>
-                            <a href="#faq">
-                                <li>
-                                    <FormattedMessage id='developers.faqTitle' />
-                                </li>
-                            </a>
-                        </SubNavigation>
-                    </div>
-                </TitleBanner>
-
-                <div className="inner">
-                    <section id="projects">
-                        <span className="nav-spacer"></span>
-                        <h2><FormattedMessage id='developers.projectsTitle' /></h2>
-                        <p className="intro">
-                            <FormattedMessage id='developers.projectsIntro' />
-                        </p>
-                        <FlexRow className="sidebar-row">
-                            <div className="body-copy column">
-                                <h3><FormattedMessage id='developers.scratchBlocksTitle' /></h3>
-                                <p>
-                                    <FormattedHTMLMessage id='developers.scratchBlocksIntro' />
-                                </p>
-                                <p>
-                                    <FormattedMessage id='developers.scratchBlocksBody' />
-                                </p>
-                            </div>
-                            <img className="sidebar column" src="/images/developers/block-sketch.png" alt="blocks" />
-                        </FlexRow>
-                        <FlexRow className="sidebar-row">
-                            <div className="body-copy column">
-                                <h3><FormattedMessage id='developers.wwwTitle' /></h3>
-                                <p>
-                                    <FormattedHTMLMessage id='developers.wwwIntro' />
-                                </p>
-                            </div>
-
-                            <img className="sidebar column" src="/images/developers/www-sketch.png" alt="www" />
-                        </FlexRow>
-                        <FlexRow className="sidebar-row">
-                            <div className="body-copy column">
-                                <h3>ScratchJr</h3>
-                                <p>
-                                    <FormattedHTMLMessage id='developers.jrBody' />
-                                </p>
-                            </div>
-                        </FlexRow>
-                    </section>
-
-                    <section id="principles">
-                        <span className="nav-spacer"></span>
-                        <h2><FormattedMessage id='developers.principlesTitle' /></h2>
-                        <p className="intro">
-                            <FormattedHTMLMessage id='developers.principlesIntro' />
-                        </p>
-
-                        <FlexRow className="sidebar-row">
-                            <div className="body-copy column">
-                                <h3><FormattedMessage id='developers.learningPrinciplesTitle' /></h3>
-                                <dl>
-                                    <dt><FormattedMessage id='developers.projectsTitle' /></dt>
-                                    <dd>
-                                        <FormattedMessage id='developers.learningPrinciplesProjectsBody' />
-                                    </dd>
-                                    <dt><FormattedMessage id='developers.learningPrinciplesPassionTitle' /></dt>
-                                    <dd>
-                                        <FormattedMessage id='developers.learningPrinciplesPassionBody' />
-                                    </dd>
-                                    <dt><FormattedMessage id='developers.learningPrinciplesPeersTitle' /></dt>
-                                    <dd>
-                                        <FormattedMessage id='developers.learningPrinciplesPeersBody' />
-                                    </dd>
-                                    <dt><FormattedMessage id='developers.learningPrinciplesPlayTitle' /></dt>
-                                    <dd>
-                                        <FormattedMessage id='developers.learningPrinciplesPlayBody' />
-                                    </dd>
-                                </dl>
-                            </div>
-                        </FlexRow>
-
-                        <FlexRow className="sidebar-row">
-                            <div className="body-copy column">
-                                <h3><FormattedMessage id='developers.designPrinciplesTitle' /></h3>
-                                <dl>
-                                    <dt><FormattedMessage id='developers.designPrinciplesRoomTitle' /></dt>
-                                    <dd>
-                                        <FormattedMessage id='developers.designPrinciplesRoomBody' />
-                                    </dd>
-                                    <dt><FormattedMessage id='developers.designPrinciplesSimpleTitle' /></dt>
-                                    <dd>
-                                        <FormattedMessage id='developers.designPrinciplesSimpleBody' />
-                                    </dd>
-                                    <dt><FormattedMessage id='developers.designPrinciplesGlobalTitle' /></dt>
-                                    <dd>
-                                        <FormattedMessage id='developers.designPrinciplesGlobalBody' />
-                                    </dd>
-                                    <dt><FormattedMessage id='developers.designPrinciplesTinkerTitle' /></dt>
-                                    <dd>
-                                        <FormattedMessage id='developers.designPrinciplesTinkerBody' />
-                                    </dd>
-                                </dl>
-                            </div>
-                        </FlexRow>
-                    </section>
-
-                    <section id="join">
-                        <span className="nav-spacer"></span>
-                        <h2><FormattedMessage id='developers.joinTitle' /></h2>
-                        <p><FormattedHTMLMessage id='developers.joinBody' /></p>
-                    </section>
-
-                    <section id="donate">
-                        <span className="nav-spacer"></span>
-                        <h2><FormattedMessage id='developers.donateTitle' /></h2>
-                        <p>
-                            <FormattedHTMLMessage id='developers.donateIntro' />
-                        </p>
-                        <p>
-                            <FormattedMessage id='developers.donateBody' />
-                        </p>
-                        <p>
-                            <FormattedMessage id='developers.donateThanks' />
-                        </p>
-                    </section>
-
-                    <section id="partners">
-                        <span className="nav-spacer"></span>
-                        <h3><FormattedMessage id='developers.partnersTitle' /></h3>
-                        <p>
-                            <FormattedMessage id='developers.partnersIntro' />
-                        </p>
-
-                        <FlexRow className="logos">
-                            <img className="logo" src="/images/developers/google.png" alt="google" />
-                            <img className="logo" src="/images/developers/intel.png" alt="intel" />
-                            <img className="logo" src="/images/developers/cn.png" alt="cartoon network" />
-                            <img className="logo" src="/images/developers/lemann.png" alt="lemann foundation" />
-                        </FlexRow>
-                    </section>
-                </div>
-
-                <TitleBanner className="faq-banner">
-                    <div className="inner">
-                        <section id="faq">
-                            <span className="nav-spacer"></span>
-                            <h3><FormattedMessage id='developers.faqTitle' /></h3>
-                            <FlexRow className="three-col-row">
-                                <div className="faq column">
-                                    <h4><FormattedMessage id='developers.faqAboutTitle' /></h4>
-                                    <p>
-                                        <FormattedHTMLMessage id='developers.faqAboutBody' />
-                                    </p>
-                                </div>
-                                <div className="faq column">
-                                    <h4><FormattedMessage id='developers.faqRulesTitle' /></h4>
-                                    <p>
-                                        <FormattedMessage id='developers.faqRulesBody' />
-                                    </p>
-                                </div>
-                                <div className="faq column">
-                                    <h4>
-                                        <FormattedMessage id='developers.faqNameTitle' />
-                                    </h4>
-                                    <p>
-                                        <FormattedMessage id='developers.faqNameBody' />
-                                    </p>
-                                </div>
-                                <div className="faq column">
-                                    <h4><FormattedMessage id='developers.faqReleasesTitle' /></h4>
-                                    <p>
-                                        <FormattedMessage id='developers.faqReleasesBody' />
-                                    </p>
-                                </div>
-                                <div className="faq column">
-                                    <h4><FormattedMessage id='developers.faqDifferencesTitle' /></h4>
-                                    <p>
-                                        <FormattedMessage id='developers.faqDifferencesBody' />
-                                    </p>
-                                </div>
-                                <div className="faq column">
-                                    <h4><FormattedMessage id='developers.faqCollabTitle' /></h4>
-                                    <p>
-                                        <FormattedHTMLMessage id='developers.faqCollabBody' />
-                                    </p>
-                                </div>
-                            </FlexRow>
-                        </section>
-                    </div>
-                </TitleBanner>
+const Developers = () => (
+    <div className="developers">
+        <TitleBanner className="masthead">
+            <div className="inner">
+                <h1 className="title-banner-h1">
+                    <FormattedMessage id="developers.title" />
+                </h1>
+                <p className="title-banner-p intro">
+                    <FormattedHTMLMessage id="developers.intro" />
+                </p>
             </div>
-        );
-    }
-});
+            <div className="band">
+                <SubNavigation className="inner">
+                    <a href="#projects">
+                        <li>
+                            <FormattedMessage id="developers.projectsTitle" />
+                        </li>
+                    </a>
+                    <a href="#principles">
+                        <li>
+                            <FormattedMessage id="developers.principlesTitle" />
+                        </li>
+                    </a>
+                    <a href="#join">
+                        <li>
+                            <FormattedMessage id="developers.joinTitle" />
+                        </li>
+                    </a>
+                    <a href="#donate">
+                        <li>
+                            <FormattedMessage id="developers.donateTitle" />
+                        </li>
+                    </a>
+                    <a href="#partners">
+                        <li>
+                            <FormattedMessage id="developers.partnersTitle" />
+                        </li>
+                    </a>
+                    <a href="#faq">
+                        <li>
+                            <FormattedMessage id="developers.faqTitle" />
+                        </li>
+                    </a>
+                </SubNavigation>
+            </div>
+        </TitleBanner>
+
+        <div className="inner">
+            <section id="projects">
+                <span className="nav-spacer" />
+                <h2><FormattedMessage id="developers.projectsTitle" /></h2>
+                <p className="intro">
+                    <FormattedMessage id="developers.projectsIntro" />
+                </p>
+                <FlexRow className="sidebar-row">
+                    <div className="body-copy column">
+                        <h3><FormattedMessage id="developers.scratchBlocksTitle" /></h3>
+                        <p>
+                            <FormattedHTMLMessage id="developers.scratchBlocksIntro" />
+                        </p>
+                        <p>
+                            <FormattedMessage id="developers.scratchBlocksBody" />
+                        </p>
+                    </div>
+                    <img
+                        alt="blocks"
+                        className="sidebar column"
+                        src="/images/developers/block-sketch.png"
+                    />
+                </FlexRow>
+                <FlexRow className="sidebar-row">
+                    <div className="body-copy column">
+                        <h3><FormattedMessage id="developers.wwwTitle" /></h3>
+                        <p>
+                            <FormattedHTMLMessage id="developers.wwwIntro" />
+                        </p>
+                    </div>
+
+                    <img
+                        alt="www"
+                        className="sidebar column"
+                        src="/images/developers/www-sketch.png"
+                    />
+                </FlexRow>
+                <FlexRow className="sidebar-row">
+                    <div className="body-copy column">
+                        <h3>ScratchJr</h3>
+                        <p>
+                            <FormattedHTMLMessage id="developers.jrBody" />
+                        </p>
+                    </div>
+                </FlexRow>
+            </section>
+
+            <section id="principles">
+                <span className="nav-spacer" />
+                <h2><FormattedMessage id="developers.principlesTitle" /></h2>
+                <p className="intro">
+                    <FormattedHTMLMessage id="developers.principlesIntro" />
+                </p>
+
+                <FlexRow className="sidebar-row">
+                    <div className="body-copy column">
+                        <h3><FormattedMessage id="developers.learningPrinciplesTitle" /></h3>
+                        <dl>
+                            <dt><FormattedMessage id="developers.projectsTitle" /></dt>
+                            <dd>
+                                <FormattedMessage id="developers.learningPrinciplesProjectsBody" />
+                            </dd>
+                            <dt><FormattedMessage id="developers.learningPrinciplesPassionTitle" /></dt>
+                            <dd>
+                                <FormattedMessage id="developers.learningPrinciplesPassionBody" />
+                            </dd>
+                            <dt><FormattedMessage id="developers.learningPrinciplesPeersTitle" /></dt>
+                            <dd>
+                                <FormattedMessage id="developers.learningPrinciplesPeersBody" />
+                            </dd>
+                            <dt><FormattedMessage id="developers.learningPrinciplesPlayTitle" /></dt>
+                            <dd>
+                                <FormattedMessage id="developers.learningPrinciplesPlayBody" />
+                            </dd>
+                        </dl>
+                    </div>
+                </FlexRow>
+
+                <FlexRow className="sidebar-row">
+                    <div className="body-copy column">
+                        <h3><FormattedMessage id="developers.designPrinciplesTitle" /></h3>
+                        <dl>
+                            <dt><FormattedMessage id="developers.designPrinciplesRoomTitle" /></dt>
+                            <dd>
+                                <FormattedMessage id="developers.designPrinciplesRoomBody" />
+                            </dd>
+                            <dt><FormattedMessage id="developers.designPrinciplesSimpleTitle" /></dt>
+                            <dd>
+                                <FormattedMessage id="developers.designPrinciplesSimpleBody" />
+                            </dd>
+                            <dt><FormattedMessage id="developers.designPrinciplesGlobalTitle" /></dt>
+                            <dd>
+                                <FormattedMessage id="developers.designPrinciplesGlobalBody" />
+                            </dd>
+                            <dt><FormattedMessage id="developers.designPrinciplesTinkerTitle" /></dt>
+                            <dd>
+                                <FormattedMessage id="developers.designPrinciplesTinkerBody" />
+                            </dd>
+                        </dl>
+                    </div>
+                </FlexRow>
+            </section>
+
+            <section id="join">
+                <span className="nav-spacer" />
+                <h2><FormattedMessage id="developers.joinTitle" /></h2>
+                <p><FormattedHTMLMessage id="developers.joinBody" /></p>
+            </section>
+
+            <section id="donate">
+                <span className="nav-spacer" />
+                <h2><FormattedMessage id="developers.donateTitle" /></h2>
+                <p>
+                    <FormattedHTMLMessage id="developers.donateIntro" />
+                </p>
+                <p>
+                    <FormattedMessage id="developers.donateBody" />
+                </p>
+                <p>
+                    <FormattedMessage id="developers.donateThanks" />
+                </p>
+            </section>
+
+            <section id="partners">
+                <span className="nav-spacer" />
+                <h3><FormattedMessage id="developers.partnersTitle" /></h3>
+                <p>
+                    <FormattedMessage id="developers.partnersIntro" />
+                </p>
+
+                <FlexRow className="logos">
+                    <img
+                        alt="google"
+                        className="logo"
+                        src="/images/developers/google.png"
+                    />
+                    <img
+                        alt="intel"
+                        className="logo"
+                        src="/images/developers/intel.png"
+                    />
+                    <img
+                        alt="cartoon network"
+                        className="logo"
+                        src="/images/developers/cn.png"
+                    />
+                    <img
+                        alt="lemann foundation"
+                        className="logo"
+                        src="/images/developers/lemann.png"
+                    />
+                </FlexRow>
+            </section>
+        </div>
+
+        <TitleBanner className="faq-banner">
+            <div className="inner">
+                <section id="faq">
+                    <span className="nav-spacer" />
+                    <h3><FormattedMessage id="developers.faqTitle" /></h3>
+                    <FlexRow className="three-col-row">
+                        <div className="faq column">
+                            <h4><FormattedMessage id="developers.faqAboutTitle" /></h4>
+                            <p>
+                                <FormattedHTMLMessage id="developers.faqAboutBody" />
+                            </p>
+                        </div>
+                        <div className="faq column">
+                            <h4><FormattedMessage id="developers.faqRulesTitle" /></h4>
+                            <p>
+                                <FormattedMessage id="developers.faqRulesBody" />
+                            </p>
+                        </div>
+                        <div className="faq column">
+                            <h4>
+                                <FormattedMessage id="developers.faqNameTitle" />
+                            </h4>
+                            <p>
+                                <FormattedMessage id="developers.faqNameBody" />
+                            </p>
+                        </div>
+                        <div className="faq column">
+                            <h4><FormattedMessage id="developers.faqReleasesTitle" /></h4>
+                            <p>
+                                <FormattedMessage id="developers.faqReleasesBody" />
+                            </p>
+                        </div>
+                        <div className="faq column">
+                            <h4><FormattedMessage id="developers.faqDifferencesTitle" /></h4>
+                            <p>
+                                <FormattedMessage id="developers.faqDifferencesBody" />
+                            </p>
+                        </div>
+                        <div className="faq column">
+                            <h4><FormattedMessage id="developers.faqCollabTitle" /></h4>
+                            <p>
+                                <FormattedHTMLMessage id="developers.faqCollabBody" />
+                            </p>
+                        </div>
+                    </FlexRow>
+                </section>
+            </div>
+        </TitleBanner>
+    </div>
+);
 
 render(<Page><Developers /></Page>, document.getElementById('app'));
diff --git a/src/views/dmca/dmca.jsx b/src/views/dmca/dmca.jsx
index 9f3bbcd24..67771b003 100644
--- a/src/views/dmca/dmca.jsx
+++ b/src/views/dmca/dmca.jsx
@@ -1,33 +1,29 @@
-var React = require('react');
-var FormattedMessage = require('react-intl').FormattedMessage;
-var render = require('../../lib/render.jsx');
+const React = require('react');
+const FormattedMessage = require('react-intl').FormattedMessage;
 
-var InformationPage = require('../../components/informationpage/informationpage.jsx');
-var Page = require('../../components/page/www/page.jsx');
+const InformationPage = require('../../components/informationpage/informationpage.jsx');
 
-var Dmca = React.createClass({
-    type: 'Dmca',
-    render: function () {
-        return (
-                <InformationPage title={'DMCA'}>
-                    <div className="inner info-inner">
-                        <p><FormattedMessage id='dmca.intro' /></p>
-                        <p>
-                            Copyright Agent / Mitchel Resnick<br/>
-                            MIT Media Laboratory<br/>
-                            77 Massachusetts Ave<br/>
-                            Room E14-445A<br/>
-                            Cambridge, MA 02139<br/>
-                            Tel: (617) 253-9783
-                        </p>
-                        <p><FormattedMessage id='dmca.llkresponse' /></p>
-                        <p><FormattedMessage id='dmca.assessment' /></p>
-                        <p><FormattedMessage id='dmca.eyetoeye' /></p>
-                        <p><FormattedMessage id='dmca.afterfiling' /></p>
-                    </div>
-                </InformationPage>
-        );
-    }
-});
+const Page = require('../../components/page/www/page.jsx');
+const render = require('../../lib/render.jsx');
+
+const Dmca = () => (
+    <InformationPage title={'DMCA'}>
+        <div className="inner info-inner">
+            <p><FormattedMessage id="dmca.intro" /></p>
+            <p>
+                Copyright Agent / Mitchel Resnick<br />
+                MIT Media Laboratory<br />
+                77 Massachusetts Ave<br />
+                Room E14-445A<br />
+                Cambridge, MA 02139<br />
+                Tel: (617) 253-9783
+            </p>
+            <p><FormattedMessage id="dmca.llkresponse" /></p>
+            <p><FormattedMessage id="dmca.assessment" /></p>
+            <p><FormattedMessage id="dmca.eyetoeye" /></p>
+            <p><FormattedMessage id="dmca.afterfiling" /></p>
+        </div>
+    </InformationPage>
+);
 
 render(<Page><Dmca /></Page>, document.getElementById('app'));
diff --git a/src/views/download/download.jsx b/src/views/download/download.jsx
index e607512fa..1a9b905bd 100644
--- a/src/views/download/download.jsx
+++ b/src/views/download/download.jsx
@@ -1,59 +1,62 @@
-var React = require('react');
-var render = require('../../lib/render.jsx');
+const FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
+const FormattedMessage = require('react-intl').FormattedMessage;
+const injectIntl = require('react-intl').injectIntl;
+const intlShape = require('react-intl').intlShape;
+const React = require('react');
 
-var FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
-var FormattedMessage = require('react-intl').FormattedMessage;
-var injectIntl = require('react-intl').injectIntl;
+const api = require('../../lib/api');
+const FlexRow = require('../../components/flex-row/flex-row.jsx');
+const SubNavigation = require('../../components/subnavigation/subnavigation.jsx');
+const TitleBanner = require('../../components/title-banner/title-banner.jsx');
 
-var api = require('../../lib/api');
-var Page = require('../../components/page/www/page.jsx');
-var TitleBanner = require('../../components/title-banner/title-banner.jsx');
-var FlexRow = require('../../components/flex-row/flex-row.jsx');
-var SubNavigation = require('../../components/subnavigation/subnavigation.jsx');
+const Page = require('../../components/page/www/page.jsx');
+const render = require('../../lib/render.jsx');
 
 require('./download.scss');
 require('../../components/forms/button.scss');
 
-var Download = injectIntl(React.createClass({
-    type: 'Download',
-    getInitialState: function () {
-        return {
+class Download extends React.Component {
+    constructor (props) {
+        super(props);
+        this.state = {
             swfVersion: ''
         };
-    },
-    componentDidMount: function () {
-        var uri = '/scratchr2/static/sa/version.xml';
+    }
+    componentDidMount () {
+        let uri = '/scratchr2/static/sa/version.xml';
         if (this.props.intl.locale === 'pt-br') {
             uri = '/scratchr2/static/sa/pt-br/version.xml';
         }
+
         api({
             host: '',
             uri: uri,
             responseType: 'string'
-        }, function (err, body, res) {
+        }, (err, body, res) => {
             if (err || res.statusCode >= 400) {
                 return this.setState({
                     swfVersion: -1
                 });
             }
 
-            var doc = new DOMParser().parseFromString(body, 'text/xml');
+            const doc = new DOMParser().parseFromString(body, 'text/xml');
             return this.setState({
                 swfVersion: doc.getElementsByTagName('versionNumber')[0].childNodes[0].nodeValue
             });
-        }.bind(this));
-    },
-    render: function () {
-        var downloadPath = '/scratchr2/static/sa/Scratch-';
+        });
+    }
+    render () {
+        let downloadPath = '/scratchr2/static/sa/Scratch-';
+        let downloadUrls = null;
         if (this.props.intl.locale === 'pt-br') {
             downloadPath = '/scratchr2/static/sa/pt-br/Scratch-';
         }
         if (this.state.swfVersion.length > 0 && this.state.swfVersion !== -1) {
-            var downloadUrls = {
-                mac: downloadPath + this.state.swfVersion + '.dmg',
-                mac105: downloadPath + this.state.swfVersion + '.air',
-                windows: downloadPath + this.state.swfVersion + '.exe',
-                linux: downloadPath + this.state.swfVersion + '.air'
+            downloadUrls = {
+                mac: `${downloadPath}${this.state.swfVersion}.dmg`,
+                mac105: `${downloadPath}${this.state.swfVersion}.air`,
+                windows: `${downloadPath}${this.state.swfVersion}.exe`,
+                linux: `${downloadPath}${this.state.swfVersion}.air`
             };
         }
 
@@ -62,73 +65,88 @@ var Download = injectIntl(React.createClass({
                 <TitleBanner className="masthead">
                     <div className="inner">
                         <h1 className="title-banner-h1">
-                            <FormattedMessage id='download.title' />
+                            <FormattedMessage id="download.title" />
                         </h1>
                         <p className="title-banner-p intro">
-                            <FormattedMessage id='download.intro' />
+                            <FormattedMessage id="download.intro" />
                         </p>
                     </div>
                     <div className="band">
                         <SubNavigation className="inner">
-                            <a href="#installation" className="sub-nav-item">
+                            <a
+                                className="sub-nav-item"
+                                href="#installation"
+                            >
                                 <li>
-                                    <FormattedMessage id='download.installation' />
+                                    <FormattedMessage id="download.installation" />
                                 </li>
                             </a>
-                            <a href="#updates" className="sub-nav-item">
+                            <a
+                                className="sub-nav-item"
+                                href="#updates"
+                            >
                                 <li>
-                                    <FormattedMessage id='download.updatesTitle' />
+                                    <FormattedMessage id="download.updatesTitle" />
                                 </li>
                             </a>
-                            <a href="#other" className="sub-nav-item">
+                            <a
+                                className="sub-nav-item"
+                                href="#other"
+                            >
                                 <li>
-                                    <FormattedMessage id='download.otherVersionsTitle' />
+                                    <FormattedMessage id="download.otherVersionsTitle" />
                                 </li>
                             </a>
-                            <a href="#issues" className="sub-nav-item">
+                            <a
+                                className="sub-nav-item"
+                                href="#issues"
+                            >
                                 <li>
-                                    <FormattedMessage id='download.knownIssuesTitle' />
+                                    <FormattedMessage id="download.knownIssuesTitle" />
                                 </li>
                             </a>
                         </SubNavigation>
                     </div>
                 </TitleBanner>
                 <div className="download-content">
-                    <section id="installation" className="installation">
+                    <section
+                        className="installation"
+                        id="installation"
+                    >
                         <div className="inner">
                             <p className="callout">
-                                <FormattedHTMLMessage id='download.introMac' />
+                                <FormattedHTMLMessage id="download.introMac" />
                             </p>
                             <FlexRow className="three-col-row">
                                 <div className="installation-column">
                                     <div className="installation-column-number">
                                         <h2 className="installation-column-number-text">{'1'}</h2>
                                     </div>
-                                    <h3><FormattedMessage id='download.airTitle' /></h3>
-                                    <p><FormattedHTMLMessage id='download.airBody' /></p>
+                                    <h3><FormattedMessage id="download.airTitle" /></h3>
+                                    <p><FormattedHTMLMessage id="download.airBody" /></p>
                                     <ul className="installation-downloads">
                                         <li className="installation-downloads-item">
-                                            <FormattedMessage id='download.macOSX' /> -
+                                            <FormattedMessage id="download.macOSX" /> -
                                             {' '}<a href="http://get.adobe.com/air/">
-                                                <FormattedMessage id='download.download' />
+                                                <FormattedMessage id="download.download" />
                                             </a>
                                         </li>
                                         <li className="installation-downloads-item">
-                                            <FormattedMessage id='download.macOlder' /> -
+                                            <FormattedMessage id="download.macOlder" /> -
                                             {' '}<a href="http://airdownload.adobe.com/air/mac/download/2.6/AdobeAIR.zip">
-                                                <FormattedMessage id='download.download' />
+                                                <FormattedMessage id="download.download" />
                                             </a>
                                         </li>
                                         <li className="installation-downloads-item">
-                                            <FormattedMessage id='download.windows' /> -
+                                            <FormattedMessage id="download.windows" /> -
                                             {' '}<a href="http://get.adobe.com/air/">
-                                                <FormattedMessage id='download.download' />
+                                                <FormattedMessage id="download.download" />
                                             </a>
                                         </li>
                                         <li className="installation-downloads-item">
-                                            <FormattedMessage id='download.linux' /> -
+                                            <FormattedMessage id="download.linux" /> -
                                             {' '}<a href="http://airdownload.adobe.com/air/lin/download/2.6/AdobeAIRInstaller.bin">
-                                                <FormattedMessage id='download.download' />
+                                                <FormattedMessage id="download.download" />
                                             </a>
                                         </li>
                                     </ul>
@@ -137,63 +155,68 @@ var Download = injectIntl(React.createClass({
                                     <div className="installation-column-number">
                                         <h2 className="installation-column-number-text">{'2'}</h2>
                                     </div>
-                                    <h3><FormattedMessage id='download.offlineEditorTitle' /></h3>
-                                    <p><FormattedMessage id='download.offlineEditorBody' /></p>
-                                    {downloadUrls ? [
-                                        <ul className="installation-downloads">
+                                    <h3><FormattedMessage id="download.offlineEditorTitle" /></h3>
+                                    <p><FormattedMessage id="download.offlineEditorBody" /></p>
+                                    {downloadUrls === null ? [] : [
+                                        <ul
+                                            className="installation-downloads"
+                                            key="installation-downloads"
+                                        >
                                             <li className="installation-downloads-item">
-                                                <FormattedMessage id='download.macOSX' /> -
+                                                <FormattedMessage id="download.macOSX" /> -
                                                 {' '}<a href={downloadUrls.mac}>
-                                                    <FormattedMessage id='download.download' />
+                                                    <FormattedMessage id="download.download" />
                                                 </a>
                                             </li>
                                             <li className="installation-downloads-item">
-                                                <FormattedMessage id='download.macOlder' /> -
+                                                <FormattedMessage id="download.macOlder" /> -
                                                 {' '}<a href={downloadUrls.mac105}>
-                                                    <FormattedMessage id='download.download' />
+                                                    <FormattedMessage id="download.download" />
                                                 </a>
                                             </li>
                                             <li className="installation-downloads-item">
-                                                <FormattedMessage id='download.windows' /> -
+                                                <FormattedMessage id="download.windows" /> -
                                                 {' '}<a href={downloadUrls.windows}>
-                                                    <FormattedMessage id='download.download' />
+                                                    <FormattedMessage id="download.download" />
                                                 </a>
                                             </li>
                                             <li className="installation-downloads-item">
-                                                <FormattedMessage id='download.linux' /> -
+                                                <FormattedMessage id="download.linux" /> -
                                                 {' '}<a href={downloadUrls.linux}>
-                                                    <FormattedMessage id='download.download' />
+                                                    <FormattedMessage id="download.download" />
                                                 </a>
                                             </li>
                                         </ul>
-                                    ] : []}
+                                    ]}
                                     {this.state.swfVersion === -1 ? [
-                                        <p><i><FormattedMessage id='download.notAvailable' /></i></p>
+                                        <p key="not-available">
+                                            <i><FormattedMessage id="download.notAvailable" /></i>
+                                        </p>
                                     ] : []}
                                 </div>
                                 <div className="installation-column">
                                     <div className="installation-column-number">
                                         <h2 className="installation-column-number-text">{'3'}</h2>
                                     </div>
-                                    <h3><FormattedMessage id='download.supportMaterialsTitle' /></h3>
-                                    <p><FormattedMessage id='download.supportMaterialsBody' /></p>
+                                    <h3><FormattedMessage id="download.supportMaterialsTitle" /></h3>
+                                    <p><FormattedMessage id="download.supportMaterialsBody" /></p>
                                     <ul className="installation-downloads">
                                         <li className="installation-downloads-item">
-                                            <FormattedMessage id='download.starterProjects' /> -
+                                            <FormattedMessage id="download.starterProjects" /> -
                                             {' '}<a href="https://scratch.mit.edu/scratchr2/static/sa/Scratch2StarterProjects.zip">
-                                                <FormattedMessage id='download.download' />
+                                                <FormattedMessage id="download.download" />
                                             </a>
                                         </li>
                                         <li className="installation-downloads-item">
-                                            <FormattedMessage id='download.gettingStarted' /> -
+                                            <FormattedMessage id="download.gettingStarted" /> -
                                             {' '}<a href="https://cdn.scratch.mit.edu/scratchr2/static/__709da8e5f3d72129538a4ccdbcbf5f2a__/pdfs/help/Getting-Started-Guide-Scratch2.pdf">
-                                                <FormattedMessage id='download.download' />
+                                                <FormattedMessage id="download.download" />
                                             </a>
                                         </li>
                                         <li className="installation-downloads-item">
-                                            <FormattedMessage id='download.scratchCards' /> -
+                                            <FormattedMessage id="download.scratchCards" /> -
                                             {' '}<a href="https://cdn.scratch.mit.edu/scratchr2/static/__709da8e5f3d72129538a4ccdbcbf5f2a__/pdfs/help/Scratch2Cards.pdf">
-                                                <FormattedMessage id='download.download' />
+                                                <FormattedMessage id="download.download" />
                                             </a>
                                         </li>
                                     </ul>
@@ -203,37 +226,40 @@ var Download = injectIntl(React.createClass({
                     </section>
                     <div className="inner">
                         <section id="updates">
-                            <span className="nav-spacer"></span>
-                            <h2><FormattedMessage id='download.updatesTitle' /></h2>
-                            <p><FormattedMessage id='download.updatesBody' /></p>
-                            {this.state.swfVersion !== -1 ? [
-                                <p>
+                            <span className="nav-spacer" />
+                            <h2><FormattedMessage id="download.updatesTitle" /></h2>
+                            <p><FormattedMessage id="download.updatesBody" /></p>
+                            {this.state.swfVersion === -1 ? [] : [
+                                <p key="current-version">
                                     <FormattedMessage
-                                        id='download.currentVersion'
+                                        id="download.currentVersion"
                                         values={{
                                             version: this.state.swfVersion
                                         }}
                                     />
                                 </p>
-                            ] : []}
+                            ]}
                         </section>
 
                         <section id="other">
-                            <span className="nav-spacer"></span>
-                            <h2><FormattedMessage id='download.otherVersionsTitle' /></h2>
-                            <p><FormattedHTMLMessage id='download.otherVersionsOlder' /></p>
-                            <p><FormattedHTMLMessage id='download.otherVersionsAdmin' /></p>
+                            <span className="nav-spacer" />
+                            <h2><FormattedMessage id="download.otherVersionsTitle" /></h2>
+                            <p><FormattedHTMLMessage id="download.otherVersionsOlder" /></p>
+                            <p><FormattedHTMLMessage id="download.otherVersionsAdmin" /></p>
                         </section>
 
                         <section id="issues">
-                            <span className="nav-spacer"></span>
-                            <h2><FormattedMessage id='download.knownIssuesTitle' /></h2>
-                            <p><FormattedMessage id='download.knownIssuesOne' /></p>
-                            <p><FormattedMessage id='download.knownIssuesTwo' /></p>
-                            <p><FormattedHTMLMessage id='download.knownIssuesThree' /></p>
-                            <p><FormattedMessage id='download.knownIssuesFour' /></p>
-                            <a href="https://scratch.mit.edu/discuss/3/" className='button mod-link'>
-                                <FormattedMessage id='download.reportBugs' />
+                            <span className="nav-spacer" />
+                            <h2><FormattedMessage id="download.knownIssuesTitle" /></h2>
+                            <p><FormattedMessage id="download.knownIssuesOne" /></p>
+                            <p><FormattedMessage id="download.knownIssuesTwo" /></p>
+                            <p><FormattedHTMLMessage id="download.knownIssuesThree" /></p>
+                            <p><FormattedMessage id="download.knownIssuesFour" /></p>
+                            <a
+                                className="button mod-link"
+                                href="https://scratch.mit.edu/discuss/3/"
+                            >
+                                <FormattedMessage id="download.reportBugs" />
                             </a>
                         </section>
                     </div>
@@ -241,6 +267,12 @@ var Download = injectIntl(React.createClass({
             </div>
         );
     }
-}));
+}
 
-render(<Page><Download /></Page>, document.getElementById('app'));
+Download.propTypes = {
+    intl: intlShape
+};
+
+const WrappedDownload = injectIntl(Download);
+
+render(<Page><WrappedDownload /></Page>, document.getElementById('app'));
diff --git a/src/views/explore/explore.jsx b/src/views/explore/explore.jsx
index 1c6ac3ca0..d8621e0be 100644
--- a/src/views/explore/explore.jsx
+++ b/src/views/explore/explore.jsx
@@ -1,27 +1,45 @@
-var classNames = require('classnames');
-var injectIntl = require('react-intl').injectIntl;
-var FormattedMessage = require('react-intl').FormattedMessage;
-var React = require('react');
-var render = require('../../lib/render.jsx');
+const bindAll = require('lodash.bindall');
+const classNames = require('classnames');
+const injectIntl = require('react-intl').injectIntl;
+const intlShape = require('react-intl').intlShape;
+const FormattedMessage = require('react-intl').FormattedMessage;
+const React = require('react');
+const render = require('../../lib/render.jsx');
 
-var api = require('../../lib/api');
+const api = require('../../lib/api');
 
-var Page = require('../../components/page/www/page.jsx');
-var Tabs = require('../../components/tabs/tabs.jsx');
-var TitleBanner = require('../../components/title-banner/title-banner.jsx');
-var Button = require('../../components/forms/button.jsx');
-var Form = require('../../components/forms/form.jsx');
-var Select = require('../../components/forms/select.jsx');
-var SubNavigation = require('../../components/subnavigation/subnavigation.jsx');
-var Grid = require('../../components/grid/grid.jsx');
+const Page = require('../../components/page/www/page.jsx');
+const Tabs = require('../../components/tabs/tabs.jsx');
+const TitleBanner = require('../../components/title-banner/title-banner.jsx');
+const Button = require('../../components/forms/button.jsx');
+const Form = require('../../components/forms/form.jsx');
+const Select = require('../../components/forms/select.jsx');
+const SubNavigation = require('../../components/subnavigation/subnavigation.jsx');
+const Grid = require('../../components/grid/grid.jsx');
 
 require('./explore.scss');
 
-// @todo migrate to React-Router once available
-var Explore = injectIntl(React.createClass({
-    type: 'Explore',
-    getDefaultProps: function () {
-        var categoryOptions = {
+class Explore extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'getExploreState',
+            'handleGetExploreMore',
+            'changeItemType',
+            'handleChangeSortMode',
+            'getBubble',
+            'getTab'
+        ]);
+
+        this.state = this.getExploreState();
+        this.state.loaded = [];
+        this.state.offset = 0;
+    }
+    componentDidMount () {
+        this.handleGetExploreMore();
+    }
+    getExploreState () {
+        const categoryOptions = {
             all: '*',
             animations: 'animations',
             art: 'art',
@@ -30,21 +48,22 @@ var Explore = injectIntl(React.createClass({
             stories: 'stories',
             tutorials: 'tutorial'
         };
-        var typeOptions = ['projects','studios'];
-        var modeOptions = ['trending', 'popular', 'recent', ''];
+        const typeOptions = ['projects', 'studios'];
+        const modeOptions = ['trending', 'popular', 'recent', ''];
 
-        var pathname = window.location.pathname.toLowerCase();
+        let pathname = window.location.pathname.toLowerCase();
         if (pathname[pathname.length - 1] === '/') {
             pathname = pathname.substring(0, pathname.length - 1);
         }
-        var options = pathname.split('/');
-        var type = options[2];
-        var currentCategory = options[3];
-        var currentMode = options.length > 4 ? options[4] : '';
+
+        const options = pathname.split('/');
+        const type = options[2];
+        const currentCategory = options[3];
+        const currentMode = options.length > 4 ? options[4] : '';
         if (Object.keys(categoryOptions).indexOf(currentCategory) === -1 ||
         typeOptions.indexOf(type) === -1 ||
         modeOptions.indexOf(currentMode) === -1){
-            window.location = window.location.origin + '/explore/projects/all/';
+            window.location = `${window.location.origin}/explore/projects/all/`;
         }
 
         return {
@@ -56,89 +75,88 @@ var Explore = injectIntl(React.createClass({
             mode: currentMode,
             loadNumber: 16
         };
-    },
-    getInitialState: function () {
-        return {
-            loaded: [],
-            offset: 0
-        };
-    },
-    componentDidMount: function () {
-        this.getExploreMore();
-    },
-    getExploreMore: function () {
-        var qText = '&q=' + this.props.acceptableTabs[this.props.category] || '*';
+    }
+    handleGetExploreMore () {
+        const qText = `&q=${this.state.acceptableTabs[this.state.category]}` || '*';
+        const mode = `&mode=${(this.state.mode ? this.state.mode : 'trending')}`;
+        const locale = this.props.intl.locale;
+        const queryString =
+            `limit=${this.state.loadNumber}&offset=${this.state.offset}&language=${locale}${mode}${qText}`;
 
         api({
-            uri: '/explore/' + this.props.itemType +
-                 '?limit=' + this.props.loadNumber +
-                 '&offset=' + this.state.offset +
-                 '&language=' + this.props.intl.locale +
-                 '&mode=' + (this.props.mode ? this.props.mode : 'trending') +
-                 qText
-        }, function (err, body) {
+            uri: `/explore/${this.state.itemType}?${queryString}`
+        }, (err, body) => {
             if (!err) {
-                var loadedSoFar = this.state.loaded;
-                Array.prototype.push.apply(loadedSoFar,body);
+                const loadedSoFar = this.state.loaded;
+                Array.prototype.push.apply(loadedSoFar, body);
                 this.setState({loaded: loadedSoFar});
-                var currentOffset = this.state.offset + this.props.loadNumber;
+                const currentOffset = this.state.offset + this.state.loadNumber;
                 this.setState({offset: currentOffset});
             }
-        }.bind(this));
-    },
-    changeItemType: function () {
-        var newType;
-        for (var t in this.props.acceptableTypes) {
-            if (this.props.itemType !== t) {
+        });
+    }
+    changeItemType () {
+        let newType;
+        for (const t of this.state.acceptableTypes) {
+            if (this.state.itemType !== t) {
                 newType = t;
                 break;
             }
         }
-        window.location = window.location.origin + '/explore/' + newType + '/' + this.props.tab + '/' + this.props.mode;
-    },
-    changeSortMode: function (name, value) {
-        if (this.props.acceptableModes.indexOf(value) !== -1) {
-            window.location = window.location.origin + '/explore/' +
-            this.props.itemType + '/' + this.props.category + '/' + value;
+        window.location = `${window.location.origin}/explore/${newType}/${this.state.tab}/${this.state.mode}`;
+    }
+    handleChangeSortMode (name, value) {
+        if (this.state.acceptableModes.indexOf(value) !== -1) {
+            window.location =
+                `${window.location.origin}/explore/${this.state.itemType}/${this.state.category}/${value}`;
         }
-    },
-    getBubble: function (type) {
-        var classes = classNames({
-            active: (this.props.category === type)
+    }
+    getBubble (type) {
+        const classes = classNames({
+            active: (this.state.category === type)
         });
         return (
-            <a href={'/explore/' + this.props.itemType + '/' + type + '/' + this.props.mode}>
+            <a href={`/explore/${this.state.itemType}/${type}/${this.state.mode}`}>
                 <li className={classes}>
-                    <FormattedMessage id={'general.' + type} />
+                    <FormattedMessage id={`general.${type}`} />
                 </li>
             </a>
         );
-    },
-    getTab: function (type) {
-        var classes = classNames({
-            active: (this.props.itemType === type)
+    }
+    getTab (type) {
+        const classes = classNames({
+            active: (this.state.itemType === type)
         });
         return (
-            <a href={'/explore/' + type + '/' + this.props.category + '/' + this.props.mode}>
+            <a href={`/explore/${type}/${this.state.category}/${this.state.mode}`}>
                 <li className={classes}>
-                    {this.props.itemType === type ? [
-                        <img src={'/svgs/tabs/' + type + '-active.svg'} className={'tab-icon ' + type} />
+                    {this.state.itemType === type ? [
+                        <img
+                            className={`tab-icon ${type}`}
+                            key={`tab-${type}`}
+                            src={`/svgs/tabs/${type}-active.svg`}
+                        />
                     ] : [
-                        <img src={'/svgs/tabs/' + type + '-inactive.svg'} className={'tab-icon ' + type} />
+                        <img
+                            className={`tab-icon ${type}`}
+                            key={`tab-${type}`}
+                            src={`/svgs/tabs/${type}-inactive.svg`}
+                        />
                     ]}
-                    <FormattedMessage id={'general.' + type} />
+                    <FormattedMessage id={`general.${type}`} />
                 </li>
             </a>
         );
-    },
-    render: function () {
-
+    }
+    render () {
         return (
             <div>
-                <div className='outer'>
+                <div className="outer">
                     <TitleBanner className="masthead">
                         <div className="inner">
-                            <h1 className="title-banner-h1"><FormattedMessage id='general.explore' /></h1>
+                            <h1 className="title-banner-h1">
+                                <FormattedMessage id="general.explore" />
+                            </h1>
                         </div>
                     </TitleBanner>
                     <Tabs>
@@ -146,7 +164,7 @@ var Explore = injectIntl(React.createClass({
                         {this.getTab('studios')}
                     </Tabs>
                     <div className="sort-controls">
-                        <SubNavigation className='categories'>
+                        <SubNavigation className="categories">
                             {this.getBubble('all')}
                             {this.getBubble('animations')}
                             {this.getBubble('art')}
@@ -155,27 +173,46 @@ var Explore = injectIntl(React.createClass({
                             {this.getBubble('stories')}
                             {this.getBubble('tutorials')}
                         </SubNavigation>
-                        <Form className='sort-mode'>
-                            <Select name="sort"
-                                    options={[
-                                        {value: 'trending', label: <FormattedMessage id='explore.trending' />},
-                                        {value: 'popular', label: <FormattedMessage id='explore.popular' />},
-                                        {value: 'recent', label: <FormattedMessage id='explore.recent' />}
-                                    ]}
-                                    value={this.props.mode}
-                                    onChange={this.changeSortMode}/>
+                        <Form className="sort-mode">
+                            <Select
+                                name="sort"
+                                options={[
+                                    {
+                                        value: 'trending',
+                                        label: this.props.intl.formatMessage({id: 'explore.trending'})
+                                    },
+                                    {
+                                        value: 'popular',
+                                        label: this.props.intl.formatMessage({id: 'explore.popular'})
+                                    },
+                                    {
+                                        value: 'recent',
+                                        label: this.props.intl.formatMessage({id: 'explore.recent'})
+                                    }
+                                ]}
+                                value={this.state.mode}
+                                onChange={this.handleChangeSortMode}
+                            />
                         </Form>
                     </div>
-                    <div id='projectBox' key='projectBox'>
-                        <Grid items={this.state.loaded}
-                              itemType={this.props.itemType}
-                              cards={true}
-                              showLoves={false}
-                              showFavorites={false}
-                              showViews={false}
-                              showAvatar={true}/>
-                          <Button onClick={this.getExploreMore} className="white">
-                            <FormattedMessage id='general.loadMore' />
+                    <div
+                        id="projectBox"
+                        key="projectBox"
+                    >
+                        <Grid
+                            cards
+                            showAvatar
+                            itemType={this.state.itemType}
+                            items={this.state.loaded}
+                            showFavorites={false}
+                            showLoves={false}
+                            showViews={false}
+                        />
+                        <Button
+                            className="white"
+                            onClick={this.handleGetExploreMore}
+                        >
+                            <FormattedMessage id="general.loadMore" />
                         </Button>
                     </div>
                 </div>
@@ -183,6 +220,12 @@ var Explore = injectIntl(React.createClass({
 
         );
     }
-}));
+}
 
-render(<Page><Explore /></Page>, document.getElementById('app'));
+Explore.propTypes = {
+    intl: intlShape
+};
+
+const WrappedExplore = injectIntl(Explore);
+
+render(<Page><WrappedExplore /></Page>, document.getElementById('app'));
diff --git a/src/views/faq/faq.jsx b/src/views/faq/faq.jsx
index b11adaa5f..44dfa5a5a 100644
--- a/src/views/faq/faq.jsx
+++ b/src/views/faq/faq.jsx
@@ -1,223 +1,223 @@
-var React = require('react');
-var injectIntl = require('react-intl').injectIntl;
-var FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
-var FormattedMessage = require('react-intl').FormattedMessage;
-var render = require('../../lib/render.jsx');
+const FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
+const FormattedMessage = require('react-intl').FormattedMessage;
+const injectIntl = require('react-intl').injectIntl;
+const intlShape = require('react-intl').intlShape;
+const React = require('react');
 
-var Page = require('../../components/page/www/page.jsx');
-var InformationPage = require('../../components/informationpage/informationpage.jsx');
+const InformationPage = require('../../components/informationpage/informationpage.jsx');
 
-var Faq = injectIntl(React.createClass({
-    type: 'Faq',
-    render: function () {
-        var formatMessage = this.props.intl.formatMessage;
-        return (
-            <InformationPage title={formatMessage({id: 'faq.title'})}>
-                <div className="inner info-inner">
-                    <section id="about-scratch">
-                        <span className="nav-spacer"></span>
-                        <h2><FormattedMessage id='faq.aboutTitle' /></h2>
-                        <dl>
-                            <dt><FormattedMessage id='faq.aboutScratchTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.aboutScratchBody' /></dd>
+const Page = require('../../components/page/www/page.jsx');
+const render = require('../../lib/render.jsx');
 
-                            <dt><FormattedMessage id='faq.makeGameTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.makeGameBody' /></dd>
+const Faq = injectIntl(props => (
+    <InformationPage title={props.intl.formatMessage({id: 'faq.title'})}>
+        <div className="inner info-inner">
+            <section id="about-scratch">
+                <span className="nav-spacer" />
+                <h2><FormattedMessage id="faq.aboutTitle" /></h2>
+                <dl>
+                    <dt><FormattedMessage id="faq.aboutScratchTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.aboutScratchBody" /></dd>
 
-                            <dt><FormattedMessage id='faq.requirementsTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.requirementsBody' /></dd>
+                    <dt><FormattedMessage id="faq.makeGameTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.makeGameBody" /></dd>
 
-                            <dt><FormattedMessage id='faq.offlineTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.offlineBody' /></dd>
+                    <dt><FormattedMessage id="faq.requirementsTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.requirementsBody" /></dd>
 
-                            <dt><FormattedMessage id='faq.uploadOldTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.uploadOldBody' /></dd>
+                    <dt><FormattedMessage id="faq.offlineTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.offlineBody" /></dd>
 
-                            <dt><FormattedMessage id='faq.recordVideoTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.recordVideoBody' /></dd>
+                    <dt><FormattedMessage id="faq.uploadOldTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.uploadOldBody" /></dd>
 
-                            <dt><FormattedMessage id='faq.scratchCostTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.scratchCostBody' /></dd>
+                    <dt><FormattedMessage id="faq.recordVideoTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.recordVideoBody" /></dd>
 
-                            <dt><FormattedMessage id='faq.mediaLabTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.mediaLabBody' /></dd>
-                        </dl>
-                    </section>
-                    <section id="privacy">
-                        <span className="nav-spacer"></span>
-                        <h2><FormattedMessage id='faq.privacyTitle' /></h2>
-                        <dl>
-                            <dt><FormattedMessage id='faq.accountInfoTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.accountInfoList' /></dd>
-                            <ul>
-                                <li><FormattedHTMLMessage id='faq.privacyUsername' /></li>
-                                <li><FormattedHTMLMessage id='faq.privacyCountry' /></li>
-                                <li><FormattedHTMLMessage id='faq.privacyBirthdate' /></li>
-                                <li><FormattedHTMLMessage id='faq.privacyGender' /></li>
-                                <li><FormattedHTMLMessage id='faq.privacyEmail' /></li>
-                            </ul>
-                            <dd><FormattedHTMLMessage id='faq.accountPublicInfo' /></dd>
-                            <dt><FormattedMessage id='faq.dataCollectionTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.dataCollectionOne' /></dd>
-                            <dt><FormattedMessage id='faq.rentInfoTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.rentInfoBody' /></dd>
-                            <dt><FormattedMessage id='faq.viewUnsharedTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.viewUnsharedBody' /></dd>
-                        </dl>
-                    </section>
-                    <section id="remix">
-                        <span className="nav-spacer"></span>
-                        <h2><FormattedMessage id='faq.remixTitle' /></h2>
-                        <dl>
-                            <dt><FormattedMessage id='faq.remixDefinitionTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.remixDefinitionBody' /></dd>
-                            <dt><FormattedMessage id='faq.remixableTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.remixableBody' /></dd>
-                            <dt><FormattedMessage id='faq.creativeCommonsTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.creativeCommonsBody' /></dd>
-                            <dt><FormattedMessage id='faq.fairUseTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.fairUseBody' /></dd>
-                        </dl>
-                    </section>
-                    <section id="accounts">
-                        <span className="nav-spacer"></span>
-                        <h2><FormattedMessage id='faq.accountsTitle' /></h2>
-                        <dl>
-                            <dt><FormattedMessage id='faq.confirmedAccountTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.confirmedAccountBody' /></dd>
-                            <dt><FormattedMessage id='faq.checkConfirmedTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.checkConfirmedBody' /></dd>
-                            <dt><FormattedMessage id='faq.howToConfirmTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.howToConfirmBody' /></dd>
-                            <dt><FormattedMessage id='faq.requireConfirmTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.requireConfirmBody' /></dd>
-                            <dt><FormattedMessage id='faq.forgotPasswordTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.forgotPasswordBody' /></dd>
-                            <dt><FormattedMessage id='faq.changePasswordTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.changePasswordBody' /></dd>
-                            <dt><FormattedMessage id='faq.changeEmailTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.changeEmailBody' /></dd>
-                            <dt><FormattedMessage id='faq.newScratcherTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.newScratcherBody' /></dd>
-                            <dt><FormattedMessage id='faq.multipleAccountTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.multipleAccountBody' /></dd>
-                            <dt><FormattedMessage id='faq.multipleLoginTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.multipleLoginBody' /></dd>
-                            <dt><FormattedMessage id='faq.changeUsernameTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.changeUsernameBody' /></dd>
-                            <dt><FormattedMessage id='faq.shareInfoTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.shareInfoBody' /></dd>
-                            <dt><FormattedMessage id='faq.deleteAccountTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.deleteAccountBody' /></dd>
-                        </dl>
-                    </section>
-                    <section id="permissions">
-                        <span className="nav-spacer"></span>
-                        <h2><FormattedMessage id='faq.permissionsTitle' /></h2>
-                        <dl>
-                            <dt><FormattedMessage id='faq.scratchFreeTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.scratchFreeBody' /></dd>
-                            <dt><FormattedMessage id='faq.scratchScreenshotTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.scratchScreenshotBody' /></dd>
-                            <dt><FormattedMessage id='faq.scratchDescriptionTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.scratchDescriptionBody' /></dd>
-                            <dt><FormattedMessage id='faq.presentScratchTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.presentScratchBody' /></dd>
-                            <dt><FormattedMessage id='faq.supportMaterialTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.supportMaterialBody' /></dd>
-                            <dt><FormattedMessage id='faq.sellProjectsTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.sellProjectsBody' /></dd>
-                            <dt><FormattedMessage id='faq.sourceCodeTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.sourceCodeBody' /></dd>
-                        </dl>
-                    </section>
-                    <section id="inappropriate-content">
-                        <span className="nav-spacer"></span>
-                        <h2><FormattedMessage id='faq.inappropriateContentTitle' /></h2>
-                        <dl>
-                            <dt><FormattedMessage id='faq.okayToShareTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.okayToShareBody' /></dd>
-                            <dt><FormattedMessage id='faq.reportContentTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.reportContentBody' /></dd>
-                            <dt><FormattedMessage id='faq.noFlameTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.noFlameBody' /></dd>
-                            <dt><FormattedMessage id='faq.reviewContentTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.reviewContentBody' /></dd>
-                            <dt><FormattedMessage id='faq.blockedAccountTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.blockedAccountBody' /></dd>
-                            <dt><FormattedMessage id='faq.stolenAccountTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.stolenAccountBody' /></dd>
-                        </dl>
-                    </section>
-                    <section id="clouddata">
-                        <span className="nav-spacer"></span>
-                        <h2><FormattedMessage id='faq.cloudDataTitle' /></h2>
-                        <dl>
-                            <dt><FormattedMessage id='faq.cloudDataInfoTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.cloudDataInfoBody' /></dd>
-                            <dt><FormattedMessage id='faq.storedCloudInfoTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.storedCloudInfoBody' /></dd>
-                            <dt><FormattedMessage id='faq.onlyNumbersTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.onlyNumbersBody' /></dd>
-                            <dt><FormattedMessage id='faq.reportCloudTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.reportCloudBody' /></dd>
-                            <dt><FormattedMessage id='faq.chatRoomTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.chatRoomBody' /></dd>
-                            <dt><FormattedMessage id='faq.makeCloudVarTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.makeCloudVarBody' /></dd>
-                            <dt><FormattedMessage id='faq.changeCloudVarTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.changeCloudVarBody' /></dd>
-                            <dt><FormattedMessage id='faq.newScratcherCloudTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.newScratcherCloudBody' /></dd>
-                            <dt><FormattedMessage id='faq.multiplayerTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.multiplayerBody' /></dd>
-                            <dt><FormattedMessage id='faq.cloudLagTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.cloudLagBody' /></dd>
-                        </dl>
-                    </section>
-                    <section id="schools">
-                        <span className="nav-spacer"></span>
-                        <h2><FormattedMessage id="faq.schoolsTitle" /></h2>
-                        <dl>
-                            <dt><FormattedMessage id='faq.howTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.howBody' /></dd>
-                            <dt><FormattedMessage id='faq.ageTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.ageBody' /></dd>
-                            <dt><FormattedMessage id='faq.noInternetTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.noInternetBody' /></dd>
-                            <dt><FormattedMessage id='faq.communityTitle' /></dt>
-                            <dd><FormattedMessage id='faq.communityBody' /></dd>
-                            <dt><FormattedMessage id='faq.teacherAccountTitle' /></dt>
-                            <dd><FormattedMessage id='faq.teacherAccountBody' /></dd>
-                            <dt><FormattedMessage id='faq.requestTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.requestBody' /></dd>
-                            <dt><FormattedMessage id='faq.edTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='faq.edBody' /></dd>
-                            <dt><FormattedMessage id='faq.dataTitle' /></dt>
-                            <dd><FormattedMessage id='faq.dataBody' /></dd>
-                            <dt><FormattedMessage id='faq.lawComplianceTitle' /></dt>
-                            <dd><FormattedMessage id='faq.lawComplianceBody' /></dd>
-                        </dl>
-                        <i><FormattedHTMLMessage id='faq.schoolsMoreInfo' /></i>
-                    </section>
-                </div>
-                <nav>
-                    <ol>
-                        <li><a href="#about-scratch"><FormattedMessage id='faq.aboutTitle' /></a></li>
-                        <li><a href="#privacy"><FormattedMessage id='faq.privacyTitle' /></a></li>
-                        <li><a href="#remix"><FormattedMessage id='faq.remixTitle' /></a></li>
-                        <li><a href="#accounts"><FormattedMessage id='faq.accountsTitle' /></a></li>
-                        <li><a href="#permissions"><FormattedMessage id='faq.permissionsTitle' /></a></li>
-                        <li><a href="#inappropriate-content">
-                            <FormattedMessage id='faq.inappropriateContentTitle' />
-                        </a></li>
-                        <li><a href="#clouddata"><FormattedMessage id='faq.cloudDataTitle' /></a></li>
-                        <li><a href="#schools"><FormattedMessage id='faq.schoolsTitle' /></a></li>
-                    </ol>
-                </nav>
-            </InformationPage>
-        );
-    }
-}));
+                    <dt><FormattedMessage id="faq.scratchCostTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.scratchCostBody" /></dd>
+
+                    <dt><FormattedMessage id="faq.mediaLabTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.mediaLabBody" /></dd>
+                </dl>
+            </section>
+            <section id="privacy">
+                <span className="nav-spacer" />
+                <h2><FormattedMessage id="faq.privacyTitle" /></h2>
+                <dl>
+                    <dt><FormattedMessage id="faq.accountInfoTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.accountInfoList" /></dd>
+                    <ul>
+                        <li><FormattedHTMLMessage id="faq.privacyUsername" /></li>
+                        <li><FormattedHTMLMessage id="faq.privacyCountry" /></li>
+                        <li><FormattedHTMLMessage id="faq.privacyBirthdate" /></li>
+                        <li><FormattedHTMLMessage id="faq.privacyGender" /></li>
+                        <li><FormattedHTMLMessage id="faq.privacyEmail" /></li>
+                    </ul>
+                    <dd><FormattedHTMLMessage id="faq.accountPublicInfo" /></dd>
+                    <dt><FormattedMessage id="faq.dataCollectionTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.dataCollectionOne" /></dd>
+                    <dt><FormattedMessage id="faq.rentInfoTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.rentInfoBody" /></dd>
+                    <dt><FormattedMessage id="faq.viewUnsharedTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.viewUnsharedBody" /></dd>
+                </dl>
+            </section>
+            <section id="remix">
+                <span className="nav-spacer" />
+                <h2><FormattedMessage id="faq.remixTitle" /></h2>
+                <dl>
+                    <dt><FormattedMessage id="faq.remixDefinitionTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.remixDefinitionBody" /></dd>
+                    <dt><FormattedMessage id="faq.remixableTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.remixableBody" /></dd>
+                    <dt><FormattedMessage id="faq.creativeCommonsTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.creativeCommonsBody" /></dd>
+                    <dt><FormattedMessage id="faq.fairUseTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.fairUseBody" /></dd>
+                </dl>
+            </section>
+            <section id="accounts">
+                <span className="nav-spacer" />
+                <h2><FormattedMessage id="faq.accountsTitle" /></h2>
+                <dl>
+                    <dt><FormattedMessage id="faq.confirmedAccountTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.confirmedAccountBody" /></dd>
+                    <dt><FormattedMessage id="faq.checkConfirmedTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.checkConfirmedBody" /></dd>
+                    <dt><FormattedMessage id="faq.howToConfirmTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.howToConfirmBody" /></dd>
+                    <dt><FormattedMessage id="faq.requireConfirmTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.requireConfirmBody" /></dd>
+                    <dt><FormattedMessage id="faq.forgotPasswordTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.forgotPasswordBody" /></dd>
+                    <dt><FormattedMessage id="faq.changePasswordTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.changePasswordBody" /></dd>
+                    <dt><FormattedMessage id="faq.changeEmailTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.changeEmailBody" /></dd>
+                    <dt><FormattedMessage id="faq.newScratcherTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.newScratcherBody" /></dd>
+                    <dt><FormattedMessage id="faq.multipleAccountTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.multipleAccountBody" /></dd>
+                    <dt><FormattedMessage id="faq.multipleLoginTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.multipleLoginBody" /></dd>
+                    <dt><FormattedMessage id="faq.changeUsernameTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.changeUsernameBody" /></dd>
+                    <dt><FormattedMessage id="faq.shareInfoTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.shareInfoBody" /></dd>
+                    <dt><FormattedMessage id="faq.deleteAccountTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.deleteAccountBody" /></dd>
+                </dl>
+            </section>
+            <section id="permissions">
+                <span className="nav-spacer" />
+                <h2><FormattedMessage id="faq.permissionsTitle" /></h2>
+                <dl>
+                    <dt><FormattedMessage id="faq.scratchFreeTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.scratchFreeBody" /></dd>
+                    <dt><FormattedMessage id="faq.scratchScreenshotTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.scratchScreenshotBody" /></dd>
+                    <dt><FormattedMessage id="faq.scratchDescriptionTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.scratchDescriptionBody" /></dd>
+                    <dt><FormattedMessage id="faq.presentScratchTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.presentScratchBody" /></dd>
+                    <dt><FormattedMessage id="faq.supportMaterialTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.supportMaterialBody" /></dd>
+                    <dt><FormattedMessage id="faq.sellProjectsTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.sellProjectsBody" /></dd>
+                    <dt><FormattedMessage id="faq.sourceCodeTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.sourceCodeBody" /></dd>
+                </dl>
+            </section>
+            <section id="inappropriate-content">
+                <span className="nav-spacer" />
+                <h2><FormattedMessage id="faq.inappropriateContentTitle" /></h2>
+                <dl>
+                    <dt><FormattedMessage id="faq.okayToShareTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.okayToShareBody" /></dd>
+                    <dt><FormattedMessage id="faq.reportContentTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.reportContentBody" /></dd>
+                    <dt><FormattedMessage id="faq.noFlameTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.noFlameBody" /></dd>
+                    <dt><FormattedMessage id="faq.reviewContentTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.reviewContentBody" /></dd>
+                    <dt><FormattedMessage id="faq.blockedAccountTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.blockedAccountBody" /></dd>
+                    <dt><FormattedMessage id="faq.stolenAccountTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.stolenAccountBody" /></dd>
+                </dl>
+            </section>
+            <section id="clouddata">
+                <span className="nav-spacer" />
+                <h2><FormattedMessage id="faq.cloudDataTitle" /></h2>
+                <dl>
+                    <dt><FormattedMessage id="faq.cloudDataInfoTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.cloudDataInfoBody" /></dd>
+                    <dt><FormattedMessage id="faq.storedCloudInfoTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.storedCloudInfoBody" /></dd>
+                    <dt><FormattedMessage id="faq.onlyNumbersTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.onlyNumbersBody" /></dd>
+                    <dt><FormattedMessage id="faq.reportCloudTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.reportCloudBody" /></dd>
+                    <dt><FormattedMessage id="faq.chatRoomTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.chatRoomBody" /></dd>
+                    <dt><FormattedMessage id="faq.makeCloudVarTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.makeCloudVarBody" /></dd>
+                    <dt><FormattedMessage id="faq.changeCloudVarTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.changeCloudVarBody" /></dd>
+                    <dt><FormattedMessage id="faq.newScratcherCloudTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.newScratcherCloudBody" /></dd>
+                    <dt><FormattedMessage id="faq.multiplayerTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.multiplayerBody" /></dd>
+                    <dt><FormattedMessage id="faq.cloudLagTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.cloudLagBody" /></dd>
+                </dl>
+            </section>
+            <section id="schools">
+                <span className="nav-spacer" />
+                <h2><FormattedMessage id="faq.schoolsTitle" /></h2>
+                <dl>
+                    <dt><FormattedMessage id="faq.howTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.howBody" /></dd>
+                    <dt><FormattedMessage id="faq.ageTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.ageBody" /></dd>
+                    <dt><FormattedMessage id="faq.noInternetTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.noInternetBody" /></dd>
+                    <dt><FormattedMessage id="faq.communityTitle" /></dt>
+                    <dd><FormattedMessage id="faq.communityBody" /></dd>
+                    <dt><FormattedMessage id="faq.teacherAccountTitle" /></dt>
+                    <dd><FormattedMessage id="faq.teacherAccountBody" /></dd>
+                    <dt><FormattedMessage id="faq.requestTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.requestBody" /></dd>
+                    <dt><FormattedMessage id="faq.edTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="faq.edBody" /></dd>
+                    <dt><FormattedMessage id="faq.dataTitle" /></dt>
+                    <dd><FormattedMessage id="faq.dataBody" /></dd>
+                    <dt><FormattedMessage id="faq.lawComplianceTitle" /></dt>
+                    <dd><FormattedMessage id="faq.lawComplianceBody" /></dd>
+                </dl>
+                <i><FormattedHTMLMessage id="faq.schoolsMoreInfo" /></i>
+            </section>
+        </div>
+        <nav>
+            <ol>
+                <li><a href="#about-scratch"><FormattedMessage id="faq.aboutTitle" /></a></li>
+                <li><a href="#privacy"><FormattedMessage id="faq.privacyTitle" /></a></li>
+                <li><a href="#remix"><FormattedMessage id="faq.remixTitle" /></a></li>
+                <li><a href="#accounts"><FormattedMessage id="faq.accountsTitle" /></a></li>
+                <li><a href="#permissions"><FormattedMessage id="faq.permissionsTitle" /></a></li>
+                <li><a href="#inappropriate-content">
+                    <FormattedMessage id="faq.inappropriateContentTitle" />
+                </a></li>
+                <li><a href="#clouddata"><FormattedMessage id="faq.cloudDataTitle" /></a></li>
+                <li><a href="#schools"><FormattedMessage id="faq.schoolsTitle" /></a></li>
+            </ol>
+        </nav>
+    </InformationPage>
+));
+
+Faq.propTypes = {
+    intl: intlShape
+};
 
 render(<Page><Faq /></Page>, document.getElementById('app'));
diff --git a/src/views/guidelines/guidelines.jsx b/src/views/guidelines/guidelines.jsx
index 5470148a7..0ff09e6b2 100644
--- a/src/views/guidelines/guidelines.jsx
+++ b/src/views/guidelines/guidelines.jsx
@@ -1,42 +1,49 @@
-var React = require('react');
-var FormattedMessage = require('react-intl').FormattedMessage;
+const FormattedMessage = require('react-intl').FormattedMessage;
+const React = require('react');
 
-var render = require('../../lib/render.jsx');
-var Page = require('../../components/page/www/page.jsx');
-var Box = require('../../components/box/box.jsx');
+const Box = require('../../components/box/box.jsx');
+
+const Page = require('../../components/page/www/page.jsx');
+const render = require('../../lib/render.jsx');
 
 require('./guidelines.scss');
 
-var Guidelines = React.createClass({
-    type: 'Guidelines',
-    render: function () {
-        return (
-            <div className="inner guidelines">
-                <Box title={<FormattedMessage id='guidelines.title' />}>
-                    <p><FormattedMessage id='guidelines.header' className="intro" /></p>
-                    <dl>
-                        <dt><FormattedMessage id='guidelines.respectheader' /></dt>
-                        <dd><FormattedMessage id='guidelines.respectbody' /></dd>
-                        <dt><FormattedMessage id='guidelines.constructiveheader' /></dt>
-                        <dd><FormattedMessage id='guidelines.constructivebody' /></dd>
-                        <dt><FormattedMessage id='guidelines.shareheader' /></dt>
-                        <dd><FormattedMessage id='guidelines.sharebody' /></dd>
-                        <dt><FormattedMessage id='guidelines.privacyheader' /></dt>
-                        <dd><FormattedMessage id='guidelines.privacybody' /></dd>
-                        <dt><FormattedMessage id='guidelines.honestyheader' /></dt>
-                        <dd><FormattedMessage id='guidelines.honestybody' /></dd>
-                        <dt><FormattedMessage id='guidelines.friendlyheader' /></dt>
-                        <dd><FormattedMessage id='guidelines.friendlybody' /></dd>
-                    </dl>
-                    <div className="guidelines-footer">
-                        <p><FormattedMessage id='guidelines.footer' /></p>
-                        <img src="//cdn.scratch.mit.edu/scratchr2/static/images/help/spritesforcommunityguid.png"
-                            alt="sprites"/>
-                    </div>
-                </Box>
+const Guidelines = () => (
+    <div className="inner guidelines">
+        <Box
+            title={
+                <FormattedMessage id="guidelines.title" />
+            }
+        >
+            <p>
+                <FormattedMessage
+                    className="intro"
+                    id="guidelines.header"
+                />
+            </p>
+            <dl>
+                <dt><FormattedMessage id="guidelines.respectheader" /></dt>
+                <dd><FormattedMessage id="guidelines.respectbody" /></dd>
+                <dt><FormattedMessage id="guidelines.constructiveheader" /></dt>
+                <dd><FormattedMessage id="guidelines.constructivebody" /></dd>
+                <dt><FormattedMessage id="guidelines.shareheader" /></dt>
+                <dd><FormattedMessage id="guidelines.sharebody" /></dd>
+                <dt><FormattedMessage id="guidelines.privacyheader" /></dt>
+                <dd><FormattedMessage id="guidelines.privacybody" /></dd>
+                <dt><FormattedMessage id="guidelines.honestyheader" /></dt>
+                <dd><FormattedMessage id="guidelines.honestybody" /></dd>
+                <dt><FormattedMessage id="guidelines.friendlyheader" /></dt>
+                <dd><FormattedMessage id="guidelines.friendlybody" /></dd>
+            </dl>
+            <div className="guidelines-footer">
+                <p><FormattedMessage id="guidelines.footer" /></p>
+                <img
+                    alt="sprites"
+                    src="//cdn.scratch.mit.edu/scratchr2/static/images/help/spritesforcommunityguid.png"
+                />
             </div>
-        );
-    }
-});
+        </Box>
+    </div>
+);
 
 render(<Page><Guidelines /></Page>, document.getElementById('app'));
diff --git a/src/views/jobs/jobs.jsx b/src/views/jobs/jobs.jsx
index ff1a6f681..ac8e68445 100644
--- a/src/views/jobs/jobs.jsx
+++ b/src/views/jobs/jobs.jsx
@@ -1,73 +1,68 @@
-var React = require('react');
-var render = require('../../lib/render.jsx');
-var FormattedMessage = require('react-intl').FormattedMessage;
+const React = require('react');
+const render = require('../../lib/render.jsx');
+const FormattedMessage = require('react-intl').FormattedMessage;
 
-var Page = require('../../components/page/www/page.jsx');
+const Page = require('../../components/page/www/page.jsx');
 
 require('./jobs.scss');
 
-var Jobs = React.createClass({
-    type: 'Jobs',
-    render: function () {
-        return (
-            <div className="jobs">
-                <div className="top">
-                    <div className="inner">
-                        <img src="/images/jobs.png" />
-                        <h2><FormattedMessage id='jobs.titleQuestion' /></h2>
-                    </div>
-                </div>
-
-                <div className="middle">
-                    <div className="inner">
-                        <h3><FormattedMessage id='jobs.joinScratchTeam' /></h3>
-                        <p><FormattedMessage id='jobs.info' /></p>
-                        <p><FormattedMessage id='jobs.workEnvironment' /></p>
-                    </div>
-                </div>
-
-                <div className="bottom">
-                    <div className="inner">
-                        <h3><FormattedMessage id='jobs.openings' /></h3>
-                        <ul>
-                            <li>
-                                <a href="https://www.media.mit.edu/about/job-opportunities/web-designer-scratch/">
-                                    Designer
-                                </a>
-                                <span>
-                                    MIT Media Lab, Cambridge, MA
-                                </span>
-                            </li>
-                            <li>
-                                <a href="https://www.media.mit.edu/about/job-opportunities/qa-engineer-scratch/">
-                                    QA Engineer
-                                </a>
-                                <span>
-                                    MIT Media Lab, Cambridge, MA
-                                </span>
-                            </li>
-                            <li>
-                                <a href="https://www.media.mit.edu/about/job-opportunities/senior-backend-engineer-scratch-1/">
-                                    Senior Backend Engineer
-                                </a>
-                                <span>
-                                    MIT Media Lab, Cambridge, MA
-                                </span>
-                            </li>
-                            <li>
-                                <a href="https://www.media.mit.edu/about/job-opportunities/learning-resources/">
-                                    Learning Resource Designer
-                                </a>
-                                <span>
-                                    MIT Media Lab, Cambridge, MA
-                                </span>
-                            </li>
-                        </ul>
-                    </div>
-                </div>
+const Jobs = () => (
+    <div className="jobs">
+        <div className="top">
+            <div className="inner">
+                <img src="/images/jobs.png" />
+                <h2><FormattedMessage id="jobs.titleQuestion" /></h2>
             </div>
-        );
-    }
-});
+        </div>
+
+        <div className="middle">
+            <div className="inner">
+                <h3><FormattedMessage id="jobs.joinScratchTeam" /></h3>
+                <p><FormattedMessage id="jobs.info" /></p>
+                <p><FormattedMessage id="jobs.workEnvironment" /></p>
+            </div>
+        </div>
+
+        <div className="bottom">
+            <div className="inner">
+                <h3><FormattedMessage id="jobs.openings" /></h3>
+                <ul>
+                    <li>
+                        <a href="https://www.media.mit.edu/about/job-opportunities/web-designer-scratch/">
+                            Designer
+                        </a>
+                        <span>
+                            MIT Media Lab, Cambridge, MA
+                        </span>
+                    </li>
+                    <li>
+                        <a href="https://www.media.mit.edu/about/job-opportunities/qa-engineer-scratch/">
+                            QA Engineer
+                        </a>
+                        <span>
+                            MIT Media Lab, Cambridge, MA
+                        </span>
+                    </li>
+                    <li>
+                        <a href="https://www.media.mit.edu/about/job-opportunities/senior-backend-engineer-scratch-1/">
+                            Senior Backend Engineer
+                        </a>
+                        <span>
+                            MIT Media Lab, Cambridge, MA
+                        </span>
+                    </li>
+                    <li>
+                        <a href="https://www.media.mit.edu/about/job-opportunities/learning-resources/">
+                            Learning Resource Designer
+                        </a>
+                        <span>
+                            MIT Media Lab, Cambridge, MA
+                        </span>
+                    </li>
+                </ul>
+            </div>
+        </div>
+    </div>
+);
 
 render(<Page><Jobs /></Page>, document.getElementById('app'));
diff --git a/src/views/jobs/moderator/moderator.jsx b/src/views/jobs/moderator/moderator.jsx
index a6af73d3c..af5fc7e2f 100644
--- a/src/views/jobs/moderator/moderator.jsx
+++ b/src/views/jobs/moderator/moderator.jsx
@@ -1,94 +1,89 @@
-var React = require('react');
-var render = require('../../../lib/render.jsx');
+const React = require('react');
 
-var Page = require('../../../components/page/www/page.jsx');
-var InformationPage = require('../../../components/informationpage/informationpage.jsx');
+const InformationPage = require('../../../components/informationpage/informationpage.jsx');
 
-var Moderator = React.createClass({
-    type: 'Moderator',
-    render: function () {
-        return (
-            <InformationPage title={'Community Moderator'}>
-                <div className="inner info-inner">
-                    <p>
-                        Interested in kids, creativity, and online communities?
-                         We're seeking community moderators to work with the Scratch
-                         Team. Moderators will support creative activities and
-                         positive interactions on Scratch — a free online community
-                         where young people program and share interactive stories,
-                         games, and animations. Scratch has grown to more than 20
-                         million registered members (ages 8 and up), creating and
-                         sharing thousands of projects each day. Moderators will
-                         gain valuable experience working online with youth in a
-                         creative, interest driven setting.
-                    </p>
-                    <h3>Responsibilities:</h3>
-                    <ul>
-                        <li>
-                            Participate actively in the Scratch online community as
-                             a mentor and resource for youth
-                        </li>
-                        <li>
-                            Help moderate projects, studios, and comments on the
-                             website
-                        </li>
-                        <li>
-                            Support youth volunteer programs in the online community
-                        </li>
-                        <li>
-                            Promote the values and core ideas of the Scratch project
-                             (such as remixing, creative collaboration, and
-                             constructive feedback)
-                        </li>
-                    </ul>
-                    <br/>
-                    <h3>Qualifications:</h3>
-                    <ul>
-                        <li>
-                            Active participation in online communities, forums, or
-                             other web-based media
-                        </li>
-                        <li>
-                            Excellent writing and communication skills
-                        </li>
-                        <li>
-                            Good at considering issues from multiple perspectives
-                        </li>
-                        <li>
-                            Able to work independently and as part of a team
-                        </li>
-                        <li>
-                            Interest in visual arts, programming, or teaching
-                        </li>
-                        <li>
-                            Not required, but would be cool: Ability to speak another
-                             language
-                        </li>
-                    </ul>
-                    <br/>
-                    <p>
-                        This position is part-time (10-12 hours per week) under contract.
-                         All candidates must be at least 18 years old and have
-                         authorization to work in the United States.
-                    </p>
-                    <p><b>(MIT Media Lab, Cambridge, MA or Remote)</b></p>
-                    <p>
-                        Send a copy of your resume, links to one or more of your online
-                         presences, and cover letter to{' '}
-                         <a href="mailto:jobs+moderator@scratch.mit.edu">
-                             jobs+moderator@scratch.mit.edu</a>.
-                    </p>
-                    <p>
-                        <i>
-                            Really want the gig? <a href="/create">Create</a> an
-                             awesome Scratch project to introduce yourself, share
-                             it on the Scratch website, and send us a link.
-                        </i>
-                    </p>
-                </div>
-            </InformationPage>
-        );
-    }
-});
+const Page = require('../../../components/page/www/page.jsx');
+const render = require('../../../lib/render.jsx');
+
+const Moderator = () => (
+    <InformationPage title={'Community Moderator'}>
+        <div className="inner info-inner">
+            <p>
+                Interested in kids, creativity, and online communities?
+                We&#39;re seeking community moderators to work with the Scratch
+                Team. Moderators will support creative activities and
+                positive interactions on Scratch — a free online community
+                where young people program and share interactive stories,
+                games, and animations. Scratch has grown to more than 20
+                million registered members (ages 8 and up), creating and
+                sharing thousands of projects each day. Moderators will
+                gain valuable experience working online with youth in a
+                creative, interest driven setting.
+            </p>
+            <h3>Responsibilities:</h3>
+            <ul>
+                <li>
+                    Participate actively in the Scratch online community as
+                    a mentor and resource for youth
+                </li>
+                <li>
+                    Help moderate projects, studios, and comments on the
+                    website
+                </li>
+                <li>
+                    Support youth volunteer programs in the online community
+                </li>
+                <li>
+                    Promote the values and core ideas of the Scratch project
+                    (such as remixing, creative collaboration, and
+                    constructive feedback)
+                </li>
+            </ul>
+            <br />
+            <h3>Qualifications:</h3>
+            <ul>
+                <li>
+                    Active participation in online communities, forums, or
+                    other web-based media
+                </li>
+                <li>
+                    Excellent writing and communication skills
+                </li>
+                <li>
+                    Good at considering issues from multiple perspectives
+                </li>
+                <li>
+                    Able to work independently and as part of a team
+                </li>
+                <li>
+                    Interest in visual arts, programming, or teaching
+                </li>
+                <li>
+                    Not required, but would be cool: Ability to speak another
+                    language
+                </li>
+            </ul>
+            <br />
+            <p>
+                This position is part-time (10-12 hours per week) under contract.
+                All candidates must be at least 18 years old and have
+                authorization to work in the United States.
+            </p>
+            <p><b>(MIT Media Lab, Cambridge, MA or Remote)</b></p>
+            <p>
+                Send a copy of your resume, links to one or more of your online
+                presences, and cover letter to{' '}
+                <a href="mailto:jobs+moderator@scratch.mit.edu">jobs+moderator@scratch.mit.edu</a>.
+            </p>
+            <p>
+                <i>
+                    Really want the gig? <a href="/create">Create</a> an
+                    awesome Scratch project to introduce yourself, share
+                    it on the Scratch website, and send us a link.
+                </i>
+            </p>
+        </div>
+    </InformationPage>
+);
 
 render(<Page><Moderator /></Page>, document.getElementById('app'));
diff --git a/src/views/messages/container.jsx b/src/views/messages/container.jsx
index c747ef15e..746aea9a3 100644
--- a/src/views/messages/container.jsx
+++ b/src/views/messages/container.jsx
@@ -1,30 +1,52 @@
-var connect = require('react-redux').connect;
-var React = require('react');
+const bindAll = require('lodash.bindall');
+const connect = require('react-redux').connect;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var messageActions = require('../../redux/messages.js');
-var render = require('../../lib/render.jsx');
-var sessionActions = require('../../redux/session.js');
+const MessagesPresentation = require('./presentation.jsx');
 
-var Page = require('../../components/page/www/page.jsx');
-var MessagesPresentation = require('./presentation.jsx');
+const messageActions = require('../../redux/messages.js');
+const sessionActions = require('../../redux/session.js');
 
-var Messages = React.createClass({
-    type: 'ConnectedMessages',
-    getInitialState: function () {
-        return {
+const Page = require('../../components/page/www/page.jsx');
+const render = require('../../lib/render.jsx');
+
+class Messages extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleFilterClick',
+            'handleMessageDismiss',
+            'handleLoadMoreMessages'
+        ]);
+        this.state = {
             filter: ''
         };
-    },
-    getDefaultProps: function () {
-        return {
-            sessionStatus: sessionActions.Status.NOT_FETCHED,
-            user: {},
-            flags: {},
-            messageOffset: 0,
-            numNewMessages: 0
-        };
-    },
-    componentDidUpdate: function (prevProps) {
+    }
+    componentDidMount () {
+        if (this.props.user.token) {
+            this.props.dispatch(
+                messageActions.getMessages(
+                    this.props.user.username,
+                    this.props.user.token,
+                    {
+                        messages: this.props.messages,
+                        offset: this.props.messageOffset,
+                        filter: this.state.filter
+                    }
+                )
+            );
+            this.props.dispatch(
+                messageActions.getAdminMessages(
+                    this.props.user.username, this.props.user.token, this.props.messageOffset
+                )
+            );
+            this.props.dispatch(
+                messageActions.getScractherInvite(this.props.user.username, this.props.user.token)
+            );
+        }
+    }
+    componentDidUpdate (prevProps) {
         if (this.props.user.username !== prevProps.user.username) {
             if (this.props.user.token) {
                 this.props.dispatch(
@@ -54,31 +76,8 @@ var Messages = React.createClass({
                 this.props.dispatch(messageActions.setMessagesOffset(0));
             }
         }
-    },
-    componentDidMount: function () {
-        if (this.props.user.token) {
-            this.props.dispatch(
-                messageActions.getMessages(
-                    this.props.user.username,
-                    this.props.user.token,
-                    {
-                        messages: this.props.messages,
-                        offset: this.props.messageOffset,
-                        filter: this.state.filter
-                    }
-                )
-            );
-            this.props.dispatch(
-                messageActions.getAdminMessages(
-                    this.props.user.username, this.props.user.token, this.props.messageOffset
-                )
-            );
-            this.props.dispatch(
-                messageActions.getScractherInvite(this.props.user.username, this.props.user.token)
-            );
-        }
-    },
-    handleFilterClick: function (field, choice) {
+    }
+    handleFilterClick (field, choice) {
         if (this.props.user.token) {
             this.props.dispatch(
                 messageActions.getMessages(
@@ -92,9 +91,9 @@ var Messages = React.createClass({
             );
         }
         this.setState({filter: choice});
-    },
-    handleMessageDismiss: function (messageType, messageId) {
-        var adminMessages = null;
+    }
+    handleMessageDismiss (messageType, messageId) {
+        let adminMessages = null;
         if (messageType === 'notification') {
             adminMessages = this.props.adminMessages;
         }
@@ -103,8 +102,8 @@ var Messages = React.createClass({
                 messageType, messageId, this.props.numNewMessages, adminMessages
             )
         );
-    },
-    handleLoadMoreMessages: function () {
+    }
+    handleLoadMoreMessages () {
         this.props.dispatch(
             messageActions.getMessages(
                 this.props.user.username,
@@ -117,47 +116,79 @@ var Messages = React.createClass({
                 }
             )
         );
-    },
-    render: function () {
-        var loadMore = true;
+    }
+    render () {
+        let loadMore = true;
         if (this.props.messageOffset > this.props.messages.length && this.props.messageOffset > 0) {
             loadMore = false;
         }
 
-        return(
+        return (
             <MessagesPresentation
+                adminMessages={this.props.adminMessages}
+                filter={this.props.filter}
+                loadMore={loadMore}
+                messages={this.props.messages}
+                numNewMessages={this.props.numNewMessages}
+                requestStatus={this.props.requestStatus}
+                scratcherInvite={this.props.invite}
                 sessionStatus={this.props.sessionStatus}
                 user={this.props.user}
-                messages={this.props.messages}
-                adminMessages={this.props.adminMessages}
-                scratcherInvite={this.props.invite}
-                numNewMessages={this.props.numNewMessages}
-                handleFilterClick={this.handleFilterClick}
-                handleAdminDismiss={this.handleMessageDismiss}
-                loadMore={loadMore}
-                loadMoreMethod={this.handleLoadMoreMessages}
-                requestStatus={this.props.requestStatus}
-                filter={this.props.filter}
+                onAdminDismiss={this.handleMessageDismiss}
+                onFilterClick={this.handleFilterClick}
+                onLoadMoreMethod={this.handleLoadMoreMessages}
             />
         );
     }
-});
+}
 
-var mapStateToProps = function (state) {
-    return {
-        sessionStatus: state.session.status,
-        user: state.session.session.user,
-        flags: state.session.session.flags,
-        numNewMessages: state.messageCount.messageCount,
-        messages: state.messages.messages.social,
-        adminMessages: state.messages.messages.admin,
-        invite: state.messages.messages.invite,
-        messageOffset: state.messages.messages.socialOffset,
-        requestStatus: state.messages.status
-    };
+Messages.propTypes = {
+    adminMessages: PropTypes.arrayOf(PropTypes.object),
+    dispatch: PropTypes.func.isRequired,
+    filter: PropTypes.string,
+    invite: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
+    messageOffset: PropTypes.number.isRequired,
+    messages: PropTypes.arrayOf(PropTypes.object),
+    numNewMessages: PropTypes.number.isRequired,
+    requestStatus: PropTypes.shape({
+        admin: PropTypes.string,
+        clear: PropTypes.string,
+        message: PropTypes.string,
+        delete: PropTypes.string
+    }).isRequired,
+    sessionStatus: PropTypes.string.isRequired,
+    user: PropTypes.shape({
+        id: PropTypes.number,
+        banned: PropTypes.bool,
+        username: PropTypes.string,
+        token: PropTypes.string,
+        thumbnailUrl: PropTypes.string,
+        dateJoined: PropTypes.string,
+        email: PropTypes.string,
+        classroomId: PropTypes.string
+    }).isRequired
 };
 
-var ConnectedMessages = connect(mapStateToProps)(Messages);
+Messages.defaultProps = {
+    messageOffset: 0,
+    numNewMessages: 0,
+    sessionStatus: sessionActions.Status.NOT_FETCHED,
+    user: {}
+};
+
+const mapStateToProps = state => ({
+    sessionStatus: state.session.status,
+    user: state.session.session.user,
+    numNewMessages: state.messageCount.messageCount,
+    messages: state.messages.messages.social,
+    adminMessages: state.messages.messages.admin,
+    invite: state.messages.messages.invite,
+    messageOffset: state.messages.messages.socialOffset,
+    requestStatus: state.messages.status
+});
+
+const ConnectedMessages = connect(mapStateToProps)(Messages);
+
 render(
     <Page><ConnectedMessages /></Page>,
     document.getElementById('app'),
diff --git a/src/views/messages/message-rows/admin-message.jsx b/src/views/messages/message-rows/admin-message.jsx
index 2d34372ec..e803a1177 100644
--- a/src/views/messages/message-rows/admin-message.jsx
+++ b/src/views/messages/message-rows/admin-message.jsx
@@ -1,42 +1,38 @@
-var FormattedDate = require('react-intl').FormattedDate;
-var React = require('react');
+const FormattedDate = require('react-intl').FormattedDate;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var Button = require('../../../components/forms/button.jsx');
-var FlexRow = require('../../../components/flex-row/flex-row.jsx');
+const Button = require('../../../components/forms/button.jsx');
+const FlexRow = require('../../../components/flex-row/flex-row.jsx');
 
-var AdminMessage = React.createClass({
-    type: 'AdminMessage',
-    propTypes: {
-        id: React.PropTypes.number.isRequired,
-        message: React.PropTypes.string.isRequired,
-        datetimeCreated: React.PropTypes.string.isRequired,
-        onDismiss: React.PropTypes.func.isRequired
-    },
-    render: function () {
-        return (
-            <li className="admin-message">
-                <FlexRow className="admin-message-header">
-                    <span className="admin-message-date">
-                        <FormattedDate value={new Date(this.props.datetimeCreated)} />
-                    </span>
-                    <Button
-                        className="mod-admin-message-dismiss"
-                        onClick={this.props.onDismiss}
-                    >
-                        <img
-                            className="admin-message-icon"
-                            src="/svgs/modal/close-x.svg"
-                            alt="close-icon"
-                        />
-                    </Button>
-                </FlexRow>
-                <p
-                    className="admin-message-content"
-                    dangerouslySetInnerHTML={{__html: this.props.message}}
+const AdminMessage = props => (
+    <li className="admin-message">
+        <FlexRow className="admin-message-header">
+            <span className="admin-message-date">
+                <FormattedDate value={new Date(props.datetimeCreated)} />
+            </span>
+            <Button
+                className="mod-admin-message-dismiss"
+                onClick={props.onDismiss}
+            >
+                <img
+                    alt="close-icon"
+                    className="admin-message-icon"
+                    src="/svgs/modal/close-x.svg"
                 />
-            </li>
-        );
-    }
-});
+            </Button>
+        </FlexRow>
+        <p
+            className="admin-message-content"
+            dangerouslySetInnerHTML={{__html: props.message}} // eslint-disable-line react/no-danger
+        />
+    </li>
+);
+
+AdminMessage.propTypes = {
+    datetimeCreated: PropTypes.string.isRequired,
+    message: PropTypes.string.isRequired,
+    onDismiss: PropTypes.func.isRequired
+};
 
 module.exports = AdminMessage;
diff --git a/src/views/messages/message-rows/become-manager.jsx b/src/views/messages/message-rows/become-manager.jsx
index 2ee8f29ba..1f5a61747 100644
--- a/src/views/messages/message-rows/become-manager.jsx
+++ b/src/views/messages/message-rows/become-manager.jsx
@@ -1,47 +1,47 @@
-var classNames = require('classnames');
-var FormattedMessage = require('react-intl').FormattedMessage;
-var React = require('react');
+const classNames = require('classnames');
+const FormattedMessage = require('react-intl').FormattedMessage;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var SocialMessage = require('../../../components/social-message/social-message.jsx');
+const SocialMessage = require('../../../components/social-message/social-message.jsx');
 
-var BecomeManagerMessage = React.createClass({
-    type: 'BecomeManagerMessage',
-    propTypes: {
-        actorUsername: React.PropTypes.string.isRequired,
-        studioId: React.PropTypes.number.isRequired,
-        studioTitle: React.PropTypes.string.isRequired,
-        datetimePromoted: React.PropTypes.string.isRequired
-    },
-    render: function () {
-        var actorUri = '/users/' + this.props.actorUsername + '/';
-        var studioUri = '/studios/' + this.props.studioId + '/';
-
-        var classes = classNames(
+const BecomeManagerMessage = props => (
+    <SocialMessage
+        className={classNames(
             'mod-become-manager',
-            this.props.className
-        );
-        return (
-            <SocialMessage
-                className={classes}
-                datetime={this.props.datetimePromoted}
-                iconSrc="/svgs/messages/owner-invite.svg"
-                iconAlt="become owner notification image"
-            >
-                <FormattedMessage
-                    id='messages.becomeManagerText'
-                    values={{
-                        username: <a
-                            href={actorUri}
-                            className="social-messages-profile-link"
-                        >
-                            {this.props.actorUsername}
-                        </a>,
-                        studio: <a href={studioUri}>{this.props.studioTitle}</a>
-                    }}
-                />
-            </SocialMessage>
-        );
-    }
-});
+            props.className
+        )}
+        datetime={props.datetimePromoted}
+        iconAlt="become owner notification image"
+        iconSrc="/svgs/messages/owner-invite.svg"
+    >
+        <FormattedMessage
+            id="messages.becomeManagerText"
+            values={{
+                username: (
+                    <a
+                        className="social-messages-profile-link"
+                        href={`/users/${props.actorUsername}/`}
+                    >
+                        {props.actorUsername}
+                    </a>
+                ),
+                studio: (
+                    <a href={`/studios/${props.studioId}/`}>
+                        {props.studioTitle}
+                    </a>
+                )
+            }}
+        />
+    </SocialMessage>
+);
+
+BecomeManagerMessage.propTypes = {
+    actorUsername: PropTypes.string.isRequired,
+    className: PropTypes.string,
+    datetimePromoted: PropTypes.string.isRequired,
+    studioId: PropTypes.number.isRequired,
+    studioTitle: PropTypes.string.isRequired
+};
 
 module.exports = BecomeManagerMessage;
diff --git a/src/views/messages/message-rows/comment-message.jsx b/src/views/messages/message-rows/comment-message.jsx
index 9981efeb7..f6df6da67 100644
--- a/src/views/messages/message-rows/comment-message.jsx
+++ b/src/views/messages/message-rows/comment-message.jsx
@@ -1,158 +1,170 @@
-var classNames = require('classnames');
-var connect = require('react-redux').connect;
-var FormattedMessage = require('react-intl').FormattedMessage;
-var injectIntl = require('react-intl').injectIntl;
-var React = require('react');
+const bindAll = require('lodash.bindall');
+const classNames = require('classnames');
+const connect = require('react-redux').connect;
+const FormattedMessage = require('react-intl').FormattedMessage;
+const injectIntl = require('react-intl').injectIntl;
+const intlShape = require('react-intl').intlShape;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var Comment = require('../../../components/comment/comment.jsx');
-var FlexRow = require('../../../components/flex-row/flex-row.jsx');
-var SocialMessage = require('../../../components/social-message/social-message.jsx');
+const Comment = require('../../../components/comment/comment.jsx');
+const FlexRow = require('../../../components/flex-row/flex-row.jsx');
+const SocialMessage = require('../../../components/social-message/social-message.jsx');
 
-var CommentMessage = injectIntl(React.createClass({
-    type: 'CommentMessage',
-    propTypes: {
-        actorUsername: React.PropTypes.string.isRequired,
-        actorId: React.PropTypes.number.isRequired,
-        objectType: React.PropTypes.oneOf([0, 1, 2]).isRequired,
-        objectId: React.PropTypes.number.isRequired,
-        commentId: React.PropTypes.number.isRequired,
-        commentText: React.PropTypes.string.isRequired,
-        commentDateTime: React.PropTypes.string.isRequired,
-        objectTitle: React.PropTypes.string,
-        commentee: React.PropTypes.string
-    },
-    getObjectLink: function (objectType, commentId, objectId) {
+class CommentMessage extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'getObjectLink',
+            'getMessageText'
+        ]);
+    }
+    getObjectLink (objectType, commentId, objectId) {
         switch (objectType) {
         case 0:
-            return '/projects/' + objectId + '/#comments-' + commentId;
+            return `/projects/${objectId}/#comments-${commentId}`;
         case 1:
-            return '/users/' + objectId + '/#comments-' + commentId;
+            return `/users/${objectId}/#comments-${commentId}`;
         case 2:
-            return '/studios/' + objectId + '/comments/#comments-' + commentId;
+            return `/studios/${objectId}/comments/#comments-${commentId}`;
         }
-    },
-    getMessageText: function (objectType, commentee) {
-        var actorLink = '/users/' + this.props.actorUsername + '/';
+    }
+    getMessageText (objectType, commentee) {
+        const actorLink = `/users/${this.props.actorUsername}/`;
         if (objectType === 2) {
             // studio comment notifications only occur for direct replies
             if (typeof commentee !== 'undefined' && commentee === this.props.user.username) {
-                var commentLink = '/studios/' + this.props.objectId + '/comments/#comments-' + this.props.commentId;
-                return <FormattedMessage
-                    id='messages.studioCommentReply'
-                    values={{
-                        profileLink: <a
-                            href={actorLink}
-                            className="social-messages-profile-link"
-                        >
-                            {this.props.actorUsername}
-                        </a>,
-                        commentLink: <a href={commentLink}>{this.props.objectTitle}</a>
-                    }}
-                />;
+                const commentLink = `/studios/${this.props.objectId}/comments/#comments-${this.props.commentId}`;
+                return (
+                    <FormattedMessage
+                        id="messages.studioCommentReply"
+                        values={{
+                            profileLink: (
+                                <a
+                                    className="social-messages-profile-link"
+                                    href={actorLink}
+                                >
+                                    {this.props.actorUsername}
+                                </a>
+                            ),
+                            commentLink: <a href={commentLink}>{this.props.objectTitle}</a>
+                        }}
+                    />
+                );
             }
         } else if (objectType === 1) {
-            var profileLink = '/users/' + this.props.objectTitle + '/#comments-' + this.props.commentId;
-            var linkText = '';
+            const profileLink = `/users/${this.props.objectTitle}/#comments-${this.props.commentId}`;
+            let linkText = '';
             if (typeof commentee !== 'undefined' && commentee === this.props.user.username) {
                 // is a profile comment, and is a reply
                 if (this.props.objectTitle === this.props.user.username) {
-                    linkText = <FormattedMessage
-                        id='messages.profileSelf'
-                    />;
+                    linkText = (<FormattedMessage id="messages.profileSelf" />);
                 } else {
-                    linkText = <FormattedMessage
-                        id='messages.profileOther'
-                        values={{
-                            username: this.props.objectTitle
-                        }}
-                    />;
+                    linkText = (
+                        <FormattedMessage
+                            id="messages.profileOther"
+                            values={{
+                                username: this.props.objectTitle
+                            }}
+                        />
+                    );
                 }
-                return <FormattedMessage
-                    id='messages.commentReply'
-                    values={{
-                        profileLink: <a
-                            href={actorLink}
-                            className="social-messages-profile-link"
-                        >
-                            {this.props.actorUsername}
-                        </a>,
-                        commentLink: <a href={profileLink}>{linkText}</a>
-                    }}
-                />;
-            } else {
-                // is a profile comment and not a reply, must be own profile
-                linkText = this.props.intl.formatMessage({
-                    id: 'messages.profileSelf'
-                });
-                return <FormattedMessage
-                    id='messages.profileComment'
-                    values={{
-                        profileLink: <a
-                            href={actorLink}
-                            className="social-messages-profile-link"
-                        >
-                            {this.props.actorUsername}
-                        </a>,
-                        commentLink: <a href={profileLink}>{linkText}</a>
-                    }}
-                />;
+                return (
+                    <FormattedMessage
+                        id="messages.commentReply"
+                        values={{
+                            profileLink: (
+                                <a
+                                    className="social-messages-profile-link"
+                                    href={actorLink}
+                                >
+                                    {this.props.actorUsername}
+                                </a>
+                            ),
+                            commentLink: <a href={profileLink}>{linkText}</a>
+                        }}
+                    />
+                );
             }
+            // is a profile comment and not a reply, must be own profile
+            linkText = this.props.intl.formatMessage({
+                id: 'messages.profileSelf'
+            });
+            return (
+                <FormattedMessage
+                    id="messages.profileComment"
+                    values={{
+                        profileLink: (
+                            <a
+                                className="social-messages-profile-link"
+                                href={actorLink}
+                            >
+                                {this.props.actorUsername}
+                            </a>
+                        ),
+                        commentLink: <a href={profileLink}>{linkText}</a>
+                    }}
+                />
+            );
         } else {
-            var projectLink = '/projects/' + this.props.objectId + '/#comments-' + this.props.commentId;
+            const projectLink = `/projects/${this.props.objectId}/#comments-${this.props.commentId}`;
             // must be a project comment, since it's not the other two, and the strict prop type reqs
             if (typeof commentee !== 'undefined' && commentee === this.props.user.username) {
-                return <FormattedMessage
-                    id='messages.commentReply'
-                    values={{
-                        profileLink: <a
-                            href={actorLink}
-                            className="social-messages-profile-link"
-                        >
-                            {this.props.actorUsername}
-                        </a>,
-                        commentLink: <a href={projectLink}>{this.props.objectTitle}</a>
-                    }}
-                />;
-            } else {
-                return <FormattedMessage
-                    id='messages.projectComment'
-                    values={{
-                        profileLink: <a
-                            href={actorLink}
-                            className="social-messages-profile-link"
-                        >
-                            {this.props.actorUsername}
-                        </a>,
-                        commentLink: <a href={projectLink}>{this.props.objectTitle}</a>
-                    }}
-                />;
+                return (
+                    <FormattedMessage
+                        id="messages.commentReply"
+                        values={{
+                            profileLink: (
+                                <a
+                                    className="social-messages-profile-link"
+                                    href={actorLink}
+                                >
+                                    {this.props.actorUsername}
+                                </a>
+                            ),
+                            commentLink: <a href={projectLink}>{this.props.objectTitle}</a>
+                        }}
+                    />
+                );
             }
+            return (
+                <FormattedMessage
+                    id="messages.projectComment"
+                    values={{
+                        profileLink: (
+                            <a
+                                className="social-messages-profile-link"
+                                href={actorLink}
+                            >
+                                {this.props.actorUsername}
+                            </a>
+                        ),
+                        commentLink: <a href={projectLink}>{this.props.objectTitle}</a>
+                    }}
+                />
+            );
         }
-    },
-    render: function () {
-        var messageText = this.getMessageText(this.props.objectType, this.props.commentee);
-        var commentorAvatar = 'https://cdn2.scratch.mit.edu/get_image/user/' + this.props.actorId + '_32x32.png';
-        var commentorAvatarAlt = this.props.actorUsername + '\'s avatar';
-        var url = '/users/' + this.props.actorUsername;
-
-        var classes = classNames(
-            'mod-comment-message',
-            this.props.className
-        );
+    }
+    render () {
         return (
             <SocialMessage
-                className={classes}
+                className={classNames(
+                    'mod-comment-message',
+                    this.props.className
+                )}
                 datetime={this.props.commentDateTime}
-                iconSrc="/svgs/messages/comment.svg"
                 iconAlt="comment notification image"
+                iconSrc="/svgs/messages/comment.svg"
             >
-                <p className="comment-message-info">{messageText}</p>
+                <p className="comment-message-info">
+                    {this.getMessageText(this.props.objectType, this.props.commentee)}
+                </p>
                 <FlexRow className="mod-comment-message">
-                    <a href={url}>
+                    <a href={`/users/${this.props.actorUsername}`}>
                         <img
+                            alt={`${this.props.actorUsername}'s avatar`}
                             className="comment-message-info-img"
-                            src={commentorAvatar}
-                            alt={commentorAvatarAlt}
+                            src={`https://cdn2.scratch.mit.edu/get_image/user/${this.props.actorId}_32x32.png`}
                         />
                     </a>
                     <Comment
@@ -162,13 +174,38 @@ var CommentMessage = injectIntl(React.createClass({
             </SocialMessage>
         );
     }
-}));
+}
 
-var mapStateToProps = function (state) {
-    return {
-        user: state.session.session.user
-    };
+CommentMessage.propTypes = {
+    actorId: PropTypes.number.isRequired,
+    actorUsername: PropTypes.string.isRequired,
+    className: PropTypes.string,
+    commentDateTime: PropTypes.string.isRequired,
+    commentId: PropTypes.number.isRequired,
+    commentText: PropTypes.string.isRequired,
+    commentee: PropTypes.string,
+    intl: intlShape.isRequired,
+    objectId: PropTypes.number.isRequired,
+    objectTitle: PropTypes.string,
+    objectType: PropTypes.oneOf([0, 1, 2]).isRequired,
+    user: PropTypes.shape({
+        id: PropTypes.number,
+        banned: PropTypes.bool,
+        username: PropTypes.string,
+        token: PropTypes.string,
+        thumbnailUrl: PropTypes.string,
+        dateJoined: PropTypes.string,
+        email: PropTypes.string,
+        classroomId: PropTypes.string
+    }).isRequired
 };
 
-var ConnectedCommentMessage = connect(mapStateToProps)(CommentMessage);
+const WrappedCommentMessage = injectIntl(CommentMessage);
+
+const mapStateToProps = state => ({
+    user: state.session.session.user
+});
+
+const ConnectedCommentMessage = connect(mapStateToProps)(WrappedCommentMessage);
+
 module.exports = ConnectedCommentMessage;
diff --git a/src/views/messages/message-rows/curator-invite.jsx b/src/views/messages/message-rows/curator-invite.jsx
index 4c9515a44..1461391f5 100644
--- a/src/views/messages/message-rows/curator-invite.jsx
+++ b/src/views/messages/message-rows/curator-invite.jsx
@@ -1,51 +1,55 @@
-var classNames = require('classnames');
-var FormattedMessage = require('react-intl').FormattedMessage;
-var injectIntl = require('react-intl').injectIntl;
-var React = require('react');
+const classNames = require('classnames');
+const FormattedMessage = require('react-intl').FormattedMessage;
+const injectIntl = require('react-intl').injectIntl;
+const intlShape = require('react-intl').intlShape;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var SocialMessage = require('../../../components/social-message/social-message.jsx');
+const SocialMessage = require('../../../components/social-message/social-message.jsx');
 
-var CuratorInviteMessage = injectIntl(React.createClass({
-    type: 'CuratorInviteMessage',
-    propTypes: {
-        actorUsername: React.PropTypes.string.isRequired,
-        studioId: React.PropTypes.number.isRequired,
-        studioTitle: React.PropTypes.string.isRequired,
-        datetimePromoted: React.PropTypes.string.isRequired
-    },
-    render: function () {
-        var studioLink = '/studios/' + this.props.studioId + '/';
-        var tabLink = '/studios/' + this.props.studioId + '/curators/';
-        var actorLink = '/users/' + this.props.actorUsername + '/';
-        var tabText = this.props.intl.formatMessage({id: 'messages.curatorTabText'});
-        
-        var classes = classNames(
+const CuratorInviteMessage = props => (
+    <SocialMessage
+        className={classNames(
             'mod-curator-invite',
-            this.props.className
-        );
-        return (
-            <SocialMessage
-                className={classes}
-                datetime={this.props.datetimePromoted}
-                iconSrc="/svgs/messages/curator-invite.svg"
-                iconAlt="curator invite notification image"
-            >
-                <FormattedMessage
-                    id='messages.curatorInviteText'
-                    values={{
-                        actorLink: <a
-                            href={actorLink}
-                            className="social-messages-profile-link"
-                        >
-                            {this.props.actorUsername}
-                        </a>,
-                        studioLink: <a href={studioLink}>{this.props.studioTitle}</a>,
-                        tabLink: <a href={tabLink}>{tabText}</a>
-                    }}
-                />
-            </SocialMessage>
-        );
-    }
-}));
+            props.className
+        )}
+        datetime={props.datetimePromoted}
+        iconAlt="curator invite notification image"
+        iconSrc="/svgs/messages/curator-invite.svg"
+    >
+        <FormattedMessage
+            id="messages.curatorInviteText"
+            values={{
+                actorLink: (
+                    <a
+                        className="social-messages-profile-link"
+                        href={`/users/${props.actorUsername}/`}
+                    >
+                        {props.actorUsername}
+                    </a>
+                ),
+                studioLink: (
+                    <a href={`/studios/${props.studioId}/`}>
+                        {props.studioTitle}
+                    </a>
+                ),
+                tabLink: (
+                    <a href={`/studios/${props.studioId}/curators/`}>
+                        {props.intl.formatMessage({id: 'messages.curatorTabText'})}
+                    </a>
+                )
+            }}
+        />
+    </SocialMessage>
+);
 
-module.exports = CuratorInviteMessage;
+CuratorInviteMessage.propTypes = {
+    actorUsername: PropTypes.string.isRequired,
+    className: PropTypes.string,
+    datetimePromoted: PropTypes.string.isRequired,
+    intl: intlShape,
+    studioId: PropTypes.number.isRequired,
+    studioTitle: PropTypes.string.isRequired
+};
+
+module.exports = injectIntl(CuratorInviteMessage);
diff --git a/src/views/messages/message-rows/favorite-project.jsx b/src/views/messages/message-rows/favorite-project.jsx
index a8459a63f..b75534eaf 100644
--- a/src/views/messages/message-rows/favorite-project.jsx
+++ b/src/views/messages/message-rows/favorite-project.jsx
@@ -1,47 +1,47 @@
-var classNames = require('classnames');
-var FormattedMessage = require('react-intl').FormattedMessage;
-var React = require('react');
+const classNames = require('classnames');
+const FormattedMessage = require('react-intl').FormattedMessage;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var SocialMessage = require('../../../components/social-message/social-message.jsx');
+const SocialMessage = require('../../../components/social-message/social-message.jsx');
 
-var FavoriteProjectMessage = React.createClass({
-    type: 'FavoriteProjectMessage',
-    propTypes: {
-        actorUsername: React.PropTypes.string.isRequired,
-        projectId: React.PropTypes.number.isRequired,
-        projectTitle: React.PropTypes.string.isRequired,
-        favoriteDateTime: React.PropTypes.string.isRequired
-    },
-    render: function () {
-        var projectLink = '/projects/' + this.props.projectId;
-        var profileLink = '/users/' + this.props.actorUsername;
-
-        var classes = classNames(
+const FavoriteProjectMessage = props => (
+    <SocialMessage
+        className={classNames(
             'mod-love-favorite',
-            this.props.className
-        );
-        return (
-            <SocialMessage
-                className={classes}
-                datetime={this.props.favoriteDateTime}
-                iconSrc="/svgs/messages/favorite.svg"
-                iconAlt="favorite notification image"
-            >
-                <FormattedMessage
-                    id='messages.favoriteText'
-                    values={{
-                        profileLink: <a
-                            href={profileLink}
-                            className="social-messages-profile-link"
-                        >
-                            {this.props.actorUsername}
-                        </a>,
-                        projectLink: <a href={projectLink}>{this.props.projectTitle}</a>
-                    }}
-                />
-            </SocialMessage>
-        );
-    }
-});
+            props.className
+        )}
+        datetime={props.favoriteDateTime}
+        iconAlt="favorite notification image"
+        iconSrc="/svgs/messages/favorite.svg"
+    >
+        <FormattedMessage
+            id="messages.favoriteText"
+            values={{
+                profileLink: (
+                    <a
+                        className="social-messages-profile-link"
+                        href={`/users/${props.actorUsername}`}
+                    >
+                        {props.actorUsername}
+                    </a>
+                ),
+                projectLink: (
+                    <a href={`/projects/${props.projectId}`}>
+                        {props.projectTitle}
+                    </a>
+                )
+            }}
+        />
+    </SocialMessage>
+);
+
+FavoriteProjectMessage.propTypes = {
+    actorUsername: PropTypes.string.isRequired,
+    className: PropTypes.string,
+    favoriteDateTime: PropTypes.string.isRequired,
+    projectId: PropTypes.number.isRequired,
+    projectTitle: PropTypes.string.isRequired
+};
 
 module.exports = FavoriteProjectMessage;
diff --git a/src/views/messages/message-rows/follow-user.jsx b/src/views/messages/message-rows/follow-user.jsx
index 32d282255..2cf67ed24 100644
--- a/src/views/messages/message-rows/follow-user.jsx
+++ b/src/views/messages/message-rows/follow-user.jsx
@@ -1,43 +1,40 @@
-var classNames = require('classnames');
-var FormattedMessage = require('react-intl').FormattedMessage;
-var React = require('react');
+const classNames = require('classnames');
+const FormattedMessage = require('react-intl').FormattedMessage;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var SocialMessage = require('../../../components/social-message/social-message.jsx');
+const SocialMessage = require('../../../components/social-message/social-message.jsx');
 
-var FollowUserMessage = React.createClass({
-    type: 'FollowUserMessage',
-    propTypes: {
-        followerUsername: React.PropTypes.string.isRequired,
-        followDateTime: React.PropTypes.string.isRequired
-    },
-    render: function () {
-        var profileLink = '/users/' + this.props.followerUsername; + '/';
-        
-        var classes = classNames(
+const FollowUserMessage = props => (
+    <SocialMessage
+        className={classNames(
             'mod-follow-user',
-            this.props.className
-        );
-        return (
-            <SocialMessage
-                className={classes}
-                datetime={this.props.followDateTime}
-                iconSrc="/svgs/messages/follow.svg"
-                iconAlt="follow notification image"
-            >
-                <FormattedMessage
-                    id='messages.followText'
-                    values={{
-                        profileLink: <a
-                            href={profileLink}
-                            className="social-messages-profile-link"
-                        >
-                            {this.props.followerUsername}
-                        </a>
-                    }}
-                />
-            </SocialMessage>
-        );
-    }
-});
+            props.className
+        )}
+        datetime={props.followDateTime}
+        iconAlt="follow notification image"
+        iconSrc="/svgs/messages/follow.svg"
+    >
+        <FormattedMessage
+            id="messages.followText"
+            values={{
+                profileLink: (
+                    <a
+                        className="social-messages-profile-link"
+                        href={`/users/${props.followerUsername}/`}
+                    >
+                        {props.followerUsername}
+                    </a>
+                )
+            }}
+        />
+    </SocialMessage>
+);
+
+FollowUserMessage.propTypes = {
+    className: PropTypes.string,
+    followDateTime: PropTypes.string.isRequired,
+    followerUsername: PropTypes.string.isRequired
+};
 
 module.exports = FollowUserMessage;
diff --git a/src/views/messages/message-rows/forum-topic-post.jsx b/src/views/messages/message-rows/forum-topic-post.jsx
index 5d25dc382..6827c7aea 100644
--- a/src/views/messages/message-rows/forum-topic-post.jsx
+++ b/src/views/messages/message-rows/forum-topic-post.jsx
@@ -1,40 +1,38 @@
-var classNames = require('classnames');
-var FormattedMessage = require('react-intl').FormattedMessage;
-var React = require('react');
+const classNames = require('classnames');
+const FormattedMessage = require('react-intl').FormattedMessage;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var SocialMessage = require('../../../components/social-message/social-message.jsx');
+const SocialMessage = require('../../../components/social-message/social-message.jsx');
 
-var ForumPostMessage = React.createClass({
-    type: 'ForumPostMessage',
-    propTypes: {
-        actorUsername: React.PropTypes.string.isRequired,
-        topicId: React.PropTypes.number.isRequired,
-        topicTitle: React.PropTypes.string.isRequired,
-        datetimeCreated: React.PropTypes.string.isRequired
-    },
-    render: function () {
-        var topicLink = '/discuss/topic/' + this.props.topicId + '/unread/';
-
-        var classes = classNames(
+const ForumPostMessage = props => (
+    <SocialMessage
+        className={classNames(
             'mod-forum-activity',
-            this.props.className
-        );
-        return (
-            <SocialMessage
-                className={classes}
-                datetime={this.props.datetimeCreated}
-                iconSrc="/svgs/messages/forum-activity.svg"
-                iconAlt="forum activity notification image"
-            >
-                <FormattedMessage
-                    id='messages.forumPostText'
-                    values={{
-                        topicLink: <a href={topicLink}>{this.props.topicTitle}</a>
-                    }}
-                />
-            </SocialMessage>
-        );
-    }
-});
+            props.className
+        )}
+        datetime={props.datetimeCreated}
+        iconAlt="forum activity notification image"
+        iconSrc="/svgs/messages/forum-activity.svg"
+    >
+        <FormattedMessage
+            id="messages.forumPostText"
+            values={{
+                topicLink: (
+                    <a href={`/discuss/topic/${props.topicId}/unread/`}>
+                        {props.topicTitle}
+                    </a>
+                )
+            }}
+        />
+    </SocialMessage>
+);
+
+ForumPostMessage.propTypes = {
+    className: PropTypes.string,
+    datetimeCreated: PropTypes.string.isRequired,
+    topicId: PropTypes.number.isRequired,
+    topicTitle: PropTypes.string.isRequired
+};
 
 module.exports = ForumPostMessage;
diff --git a/src/views/messages/message-rows/love-project.jsx b/src/views/messages/message-rows/love-project.jsx
index 02b536b15..3d5409aff 100644
--- a/src/views/messages/message-rows/love-project.jsx
+++ b/src/views/messages/message-rows/love-project.jsx
@@ -1,47 +1,47 @@
-var classNames = require('classnames');
-var FormattedMessage = require('react-intl').FormattedMessage;
-var React = require('react');
+const classNames = require('classnames');
+const FormattedMessage = require('react-intl').FormattedMessage;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var SocialMessage = require('../../../components/social-message/social-message.jsx');
+const SocialMessage = require('../../../components/social-message/social-message.jsx');
 
-var LoveProjectMessage = React.createClass({
-    type: 'LoveProjectMessage',
-    propTypes: {
-        actorUsername: React.PropTypes.string.isRequired,
-        projectId: React.PropTypes.number.isRequired,
-        projectTitle: React.PropTypes.string.isRequired,
-        loveDateTime: React.PropTypes.string.isRequired
-    },
-    render: function () {
-        var projectLink = '/projects/' + this.props.projectId;
-        var profileLink = '/users/' + this.props.actorUsername;
-
-        var classes = classNames(
+const LoveProjectMessage = props => (
+    <SocialMessage
+        className={classNames(
             'mod-love-project',
-            this.props.className
-        );
-        return (
-            <SocialMessage
-                className={classes}
-                datetime={this.props.loveDateTime}
-                iconSrc="/svgs/messages/love.svg"
-                iconAlt="love notification image"
-            >
-                <FormattedMessage
-                    id='messages.loveText'
-                    values={{
-                        profileLink: <a
-                            href={profileLink}
-                            className="social-messages-profile-link"
-                        >
-                            {this.props.actorUsername}
-                        </a>,
-                        projectLink: <a href={projectLink}>{this.props.projectTitle}</a>
-                    }}
-                />
-            </SocialMessage>
-        );
-    }
-});
+            props.className
+        )}
+        datetime={props.loveDateTime}
+        iconAlt="love notification image"
+        iconSrc="/svgs/messages/love.svg"
+    >
+        <FormattedMessage
+            id="messages.loveText"
+            values={{
+                profileLink: (
+                    <a
+                        className="social-messages-profile-link"
+                        href={`/users/${props.actorUsername}`}
+                    >
+                        {props.actorUsername}
+                    </a>
+                ),
+                projectLink: (
+                    <a href={`/projects/${props.projectId}`}>
+                        {props.projectTitle}
+                    </a>
+                )
+            }}
+        />
+    </SocialMessage>
+);
+
+LoveProjectMessage.propTypes = {
+    actorUsername: PropTypes.string.isRequired,
+    className: PropTypes.string,
+    loveDateTime: PropTypes.string.isRequired,
+    projectId: PropTypes.number.isRequired,
+    projectTitle: PropTypes.string.isRequired
+};
 
 module.exports = LoveProjectMessage;
diff --git a/src/views/messages/message-rows/remix-project.jsx b/src/views/messages/message-rows/remix-project.jsx
index e3d603870..356e6f6b5 100644
--- a/src/views/messages/message-rows/remix-project.jsx
+++ b/src/views/messages/message-rows/remix-project.jsx
@@ -1,51 +1,54 @@
-var classNames = require('classnames');
-var FormattedMessage = require('react-intl').FormattedMessage;
-var React = require('react');
+const classNames = require('classnames');
+const FormattedMessage = require('react-intl').FormattedMessage;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var SocialMessage = require('../../../components/social-message/social-message.jsx');
+const SocialMessage = require('../../../components/social-message/social-message.jsx');
 
-var RemixProjectMessage = React.createClass({
-    type: 'RemixProjectMessage',
-    propTypes: {
-        actorUsername: React.PropTypes.string.isRequired,
-        projectId: React.PropTypes.number.isRequired,
-        projectTitle: React.PropTypes.string.isRequired,
-        parentId: React.PropTypes.number.isRequired,
-        parentTitle: React.PropTypes.string.isRequired,
-        remixDate: React.PropTypes.string.isRequired
-    },
-    render: function () {
-        var projectLink = '/projects/' + this.props.projectId;
-        var profileLink = '/users/' + this.props.actorUsername;
-        var remixedProjectLink = '/projects/' + this.props.parentId;
-        
-        var classes = classNames(
+const RemixProjectMessage = props => (
+    <SocialMessage
+        className={classNames(
             'mod-remix-project',
-            this.props.className
-        );
-        return (
-            <SocialMessage
-                className={classes}
-                datetime={this.props.remixDate}
-                iconSrc="/svgs/messages/remix.svg"
-                iconAlt="remix notification image"
-            >
-                <FormattedMessage
-                    id='messages.remixText'
-                    values={{
-                        profileLink: <a
-                            href={profileLink}
-                            className="social-messages-profile-link"
-                        >
-                            {this.props.actorUsername}
-                        </a>,
-                        projectLink: <a href={projectLink}>{this.props.projectTitle}</a>,
-                        remixedProjectLink: <a href={remixedProjectLink}>{this.props.parentTitle}</a>
-                    }}
-                />
-            </SocialMessage>
-        );
-    }
-});
+            props.className
+        )}
+        datetime={props.remixDate}
+        iconAlt="remix notification image"
+        iconSrc="/svgs/messages/remix.svg"
+    >
+        <FormattedMessage
+            id="messages.remixText"
+            values={{
+                profileLink: (
+                    <a
+                        className="social-messages-profile-link"
+                        href={`/users/${props.actorUsername}`}
+                    >
+                        {props.actorUsername}
+                    </a>
+                ),
+                projectLink: (
+                    <a href={`/projects/${props.projectId}`}>
+                        {props.projectTitle}
+                    </a>
+                ),
+                remixedProjectLink: (
+                    <a href={`/projects/${props.parentId}`}>
+                        {props.parentTitle}
+                    </a>
+                )
+            }}
+        />
+    </SocialMessage>
+);
+
+RemixProjectMessage.propTypes = {
+    actorUsername: PropTypes.string.isRequired,
+    className: PropTypes.string,
+    parentId: PropTypes.number.isRequired,
+    parentTitle: PropTypes.string.isRequired,
+    projectId: PropTypes.number.isRequired,
+    projectTitle: PropTypes.string.isRequired,
+    remixDate: PropTypes.string.isRequired
+};
 
 module.exports = RemixProjectMessage;
diff --git a/src/views/messages/message-rows/scratcher-invite.jsx b/src/views/messages/message-rows/scratcher-invite.jsx
index 17d32c69f..f64641065 100644
--- a/src/views/messages/message-rows/scratcher-invite.jsx
+++ b/src/views/messages/message-rows/scratcher-invite.jsx
@@ -1,50 +1,50 @@
-var FormattedDate = require('react-intl').FormattedDate;
-var FormattedMessage = require('react-intl').FormattedMessage;
-var injectIntl = require('react-intl').injectIntl;
-var React = require('react');
+const FormattedDate = require('react-intl').FormattedDate;
+const FormattedMessage = require('react-intl').FormattedMessage;
+const injectIntl = require('react-intl').injectIntl;
+const intlShape = require('react-intl').intlShape;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var Button = require('../../../components/forms/button.jsx');
-var FlexRow = require('../../../components/flex-row/flex-row.jsx');
+const Button = require('../../../components/forms/button.jsx');
+const FlexRow = require('../../../components/flex-row/flex-row.jsx');
 
-var AdminMessage = injectIntl(React.createClass({
-    type: 'AdminMessage',
-    propTypes: {
-        id: React.PropTypes.number.isRequired,
-        username: React.PropTypes.string.isRequired,
-        datetimeCreated: React.PropTypes.string.isRequired,
-        onDismiss: React.PropTypes.func.isRequired
-    },
-    render: function () {
-        var learnMoreLink = '/users/' + this.props.username + '#promote';
-        var learnMoreMessage = this.props.intl.formatMessage({id: 'messages.learnMore'});
-        return (
-            <li className="admin-message">
-                <FlexRow className="admin-message-header">
-                    <span className="admin-message-date">
-                        <FormattedDate value={new Date(this.props.datetimeCreated)} />
-                    </span>
-                    <Button
-                        className="mod-scratcher-invite-dismiss"
-                        onClick={this.props.onDismiss}
-                    >
-                        <img
-                            className="mod-scratcher-invite-icon"
-                            src="/svgs/modal/close-x.svg"
-                            alt="close-icon"
-                        />
-                    </Button>
-                </FlexRow>
-                <p className="admin-message-content">
-                    <FormattedMessage
-                        id='messages.scratcherInvite'
-                        values={{
-                            learnMore: <a href={learnMoreLink}>{learnMoreMessage}</a>
-                        }}
-                    />
-                </p>
-            </li>
-        );
-    }
-}));
+const AdminMessage = props => (
+    <li className="admin-message">
+        <FlexRow className="admin-message-header">
+            <span className="admin-message-date">
+                <FormattedDate value={new Date(props.datetimeCreated)} />
+            </span>
+            <Button
+                className="mod-scratcher-invite-dismiss"
+                onClick={props.onDismiss}
+            >
+                <img
+                    alt="close-icon"
+                    className="mod-scratcher-invite-icon"
+                    src="/svgs/modal/close-x.svg"
+                />
+            </Button>
+        </FlexRow>
+        <p className="admin-message-content">
+            <FormattedMessage
+                id="messages.scratcherInvite"
+                values={{
+                    learnMore: (
+                        <a href={`/users/${props.username}#promote`}>
+                            {props.intl.formatMessage({id: 'messages.learnMore'})}
+                        </a>
+                    )
+                }}
+            />
+        </p>
+    </li>
+);
 
-module.exports = AdminMessage;
+AdminMessage.propTypes = {
+    datetimeCreated: PropTypes.string.isRequired,
+    intl: intlShape,
+    onDismiss: PropTypes.func.isRequired,
+    username: PropTypes.string.isRequired
+};
+
+module.exports = injectIntl(AdminMessage);
diff --git a/src/views/messages/message-rows/studio-activity.jsx b/src/views/messages/message-rows/studio-activity.jsx
index 62fd50d3e..d63c36e8f 100644
--- a/src/views/messages/message-rows/studio-activity.jsx
+++ b/src/views/messages/message-rows/studio-activity.jsx
@@ -1,39 +1,38 @@
-var classNames = require('classnames');
-var FormattedMessage = require('react-intl').FormattedMessage;
-var React = require('react');
+const classNames = require('classnames');
+const FormattedMessage = require('react-intl').FormattedMessage;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var SocialMessage = require('../../../components/social-message/social-message.jsx');
+const SocialMessage = require('../../../components/social-message/social-message.jsx');
 
-var StudioActivityMessage = React.createClass({
-    type: 'StudioActivityMessage',
-    propTypes: {
-        studioId: React.PropTypes.number.isRequired,
-        studioTitle: React.PropTypes.string.isRequired,
-        datetimeCreated: React.PropTypes.string.isRequired
-    },
-    render: function () {
-        var studioLink = '/studios/' + this.props.studioId + '/activity';
-
-        var classes = classNames(
+const StudioActivityMessage = props => (
+    <SocialMessage
+        className={classNames(
             'mod-studio-activity',
-            this.props.className
-        );
-        return (
-            <SocialMessage
-                className={classes}
-                datetime={this.props.datetimeCreated}
-                iconSrc="/svgs/messages/studio-activity.svg"
-                iconAlt="studio activity notification image"
-            >
-                <FormattedMessage
-                    id='messages.studioActivityText'
-                    values={{
-                        studioLink: <a href={studioLink}>{this.props.studioTitle}</a>
-                    }}
-                />
-            </SocialMessage>
-        );
-    }
-});
+            props.className
+        )}
+        datetime={props.datetimeCreated}
+        iconAlt="studio activity notification image"
+        iconSrc="/svgs/messages/studio-activity.svg"
+    >
+        <FormattedMessage
+            id="messages.studioActivityText"
+            values={{
+                studioLink: (
+                    <a href={`/studios/${props.studioId}/activity`}>
+                        {props.studioTitle}
+                    </a>
+                )
+            }}
+        />
+    </SocialMessage>
+);
+
+StudioActivityMessage.propTypes = {
+    className: PropTypes.string,
+    datetimeCreated: PropTypes.string.isRequired,
+    studioId: PropTypes.number.isRequired,
+    studioTitle: PropTypes.string.isRequired
+};
 
 module.exports = StudioActivityMessage;
diff --git a/src/views/messages/message-rows/user-join.jsx b/src/views/messages/message-rows/user-join.jsx
index a64b4fa1b..31b88644b 100644
--- a/src/views/messages/message-rows/user-join.jsx
+++ b/src/views/messages/message-rows/user-join.jsx
@@ -1,38 +1,42 @@
-var classNames = require('classnames');
-var FormattedMessage = require('react-intl').FormattedMessage;
-var injectIntl = require('react-intl').injectIntl;
-var React = require('react');
+const classNames = require('classnames');
+const FormattedMessage = require('react-intl').FormattedMessage;
+const injectIntl = require('react-intl').injectIntl;
+const intlShape = require('react-intl').intlShape;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var SocialMessage = require('../../../components/social-message/social-message.jsx');
+const SocialMessage = require('../../../components/social-message/social-message.jsx');
 
-var UserJoinMessage = injectIntl(React.createClass({
-    type: 'UserJoinMessage',
-    propTypes: {
-        datetimeJoined: React.PropTypes.string.isRequired
-    },
-    render: function () {
-        var exploreText = this.props.intl.formatMessage({id: 'general.explore'});
-        var projectText = this.props.intl.formatMessage({id: 'messages.userJoinMakeProject'});
-        
-        var classes = classNames(
+const UserJoinMessage = props => (
+    <SocialMessage
+        className={classNames(
             'mod-user-join',
-            this.props.className
-        );
-        return (
-            <SocialMessage
-                className={classes}
-                datetime={this.props.datetimeJoined}
-            >
-                <FormattedMessage
-                    id='messages.userJoinText'
-                    values={{
-                        exploreLink: <a href="/explore">{exploreText}</a>,
-                        makeProjectLink: <a href="/projects/editor/?tip_bar=getStarted">{projectText}</a>
-                    }}
-                />
-            </SocialMessage>
-        );
-    }
-}));
+            props.className
+        )}
+        datetime={props.datetimeJoined}
+    >
+        <FormattedMessage
+            id="messages.userJoinText"
+            values={{
+                exploreLink: (
+                    <a href="/explore">
+                        {props.intl.formatMessage({id: 'general.explore'})}
+                    </a>
+                ),
+                makeProjectLink: (
+                    <a href="/projects/editor/?tip_bar=getStarted">
+                        {props.intl.formatMessage({id: 'messages.userJoinMakeProject'})}
+                    </a>
+                )
+            }}
+        />
+    </SocialMessage>
+);
 
-module.exports = UserJoinMessage;
+UserJoinMessage.propTypes = {
+    className: PropTypes.string,
+    datetimeJoined: PropTypes.string.isRequired,
+    intl: intlShape
+};
+
+module.exports = injectIntl(UserJoinMessage);
diff --git a/src/views/messages/presentation.jsx b/src/views/messages/presentation.jsx
index 89cabefd4..49bed31cf 100644
--- a/src/views/messages/presentation.jsx
+++ b/src/views/messages/presentation.jsx
@@ -1,186 +1,210 @@
-var FormattedMessage = require('react-intl').FormattedMessage;
-var FormattedNumber = require('react-intl').FormattedNumber;
-var injectIntl = require('react-intl').injectIntl;
-var React = require('react');
+const bindAll = require('lodash.bindall');
+const FormattedMessage = require('react-intl').FormattedMessage;
+const FormattedNumber = require('react-intl').FormattedNumber;
+const injectIntl = require('react-intl').injectIntl;
+const intlShape = require('react-intl').intlShape;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var Button = require('../../components/forms/button.jsx');
-var FlexRow = require('../../components/flex-row/flex-row.jsx');
-var Form = require('../../components/forms/form.jsx');
-var Select = require('../../components/forms/select.jsx');
-var TitleBanner = require('../../components/title-banner/title-banner.jsx');
+const Button = require('../../components/forms/button.jsx');
+const FlexRow = require('../../components/flex-row/flex-row.jsx');
+const Form = require('../../components/forms/form.jsx');
+const Select = require('../../components/forms/select.jsx');
+const TitleBanner = require('../../components/title-banner/title-banner.jsx');
 
-var messageStatuses = require('../../redux/messages').Status;
+const messageStatuses = require('../../redux/messages').Status;
 
 // Message Components
-var AdminMessage = require('./message-rows/admin-message.jsx');
-var BecomeManagerMessage = require('./message-rows/become-manager.jsx');
-var CommentMessage = require('./message-rows/comment-message.jsx');
-var CuratorInviteMessage = require('./message-rows/curator-invite.jsx');
-var FavoriteProjectMessage = require('./message-rows/favorite-project.jsx');
-var FollowUserMessage = require('./message-rows/follow-user.jsx');
-var ForumPostMessage= require('./message-rows/forum-topic-post.jsx');
-var LoveProjectMessage = require('./message-rows/love-project.jsx');
-var RemixProjectMessage = require('./message-rows/remix-project.jsx');
-var ScratcherInvite = require('./message-rows/scratcher-invite.jsx');
-var StudioActivityMessage = require('./message-rows/studio-activity.jsx');
-var UserJoinMessage = require('./message-rows/user-join.jsx');
+const AdminMessage = require('./message-rows/admin-message.jsx');
+const BecomeManagerMessage = require('./message-rows/become-manager.jsx');
+const CommentMessage = require('./message-rows/comment-message.jsx');
+const CuratorInviteMessage = require('./message-rows/curator-invite.jsx');
+const FavoriteProjectMessage = require('./message-rows/favorite-project.jsx');
+const FollowUserMessage = require('./message-rows/follow-user.jsx');
+const ForumPostMessage = require('./message-rows/forum-topic-post.jsx');
+const LoveProjectMessage = require('./message-rows/love-project.jsx');
+const RemixProjectMessage = require('./message-rows/remix-project.jsx');
+const ScratcherInvite = require('./message-rows/scratcher-invite.jsx');
+const StudioActivityMessage = require('./message-rows/studio-activity.jsx');
+const UserJoinMessage = require('./message-rows/user-join.jsx');
 
 require('./messages.scss');
 
-var SocialMessagesList = React.createClass({
-    type: 'SocialMessagesList',
-    propTypes: {
-        loadStatus: React.PropTypes.string,
-        messages: React.PropTypes.array.isRequired,
-        numNewMessages: React.PropTypes.number,
-        loadMore: React.PropTypes.bool.isRequired,
-        loadMoreMethod: React.PropTypes.func
-    },
-    getDefaultProps: function () {
-        return {
-            loadStatus: messageStatuses.FETCHING,
-            numNewMessages: 0
-        };
-    },
-    getComponentForMessage: function (message, unread) {
-        var className = (unread) ? 'mod-unread' : '';
-        var key = message.type + '_' + message.id;
+class SocialMessagesList extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'getComponentForMessage',
+            'renderSocialMessages',
+            'renderLoadMore',
+            'renderMessageCounter'
+        ]);
+    }
+    getComponentForMessage (message, unread) {
+        const className = (unread) ? 'mod-unread' : '';
+        const key = `${message.type}_ ${message.id}`;
 
         switch (message.type) {
         case 'followuser':
-            return <FollowUserMessage
-                key={key}
-                className={className}
-                followerUsername={message.actor_username}
-                followDateTime={message.datetime_created}
-            />;
+            return (
+                <FollowUserMessage
+                    className={className}
+                    followDateTime={message.datetime_created}
+                    followerUsername={message.actor_username}
+                    key={key}
+                />
+            );
         case 'loveproject':
-            return <LoveProjectMessage
-                key={key}
-                className={className}
-                actorUsername={message.actor_username}
-                projectId={message.project_id}
-                projectTitle={message.title}
-                loveDateTime={message.datetime_created}
-            />;
+            return (
+                <LoveProjectMessage
+                    actorUsername={message.actor_username}
+                    className={className}
+                    key={key}
+                    loveDateTime={message.datetime_created}
+                    projectId={message.project_id}
+                    projectTitle={message.title}
+                />
+            );
         case 'favoriteproject':
-            return <FavoriteProjectMessage
-                key={key}
-                className={className}
-                actorUsername={message.actor_username}
-                projectId={message.project_id}
-                projectTitle={message.project_title}
-                favoriteDateTime={message.datetime_created}
-            />;
+            return (
+                <FavoriteProjectMessage
+                    actorUsername={message.actor_username}
+                    className={className}
+                    favoriteDateTime={message.datetime_created}
+                    key={key}
+                    projectId={message.project_id}
+                    projectTitle={message.project_title}
+                />
+            );
         case 'addcomment':
-            return <CommentMessage
-                key={key}
-                className={className}
-                actorUsername={message.actor_username}
-                actorId={message.actor_id}
-                objectType={message.comment_type}
-                objectId={message.comment_obj_id}
-                commentId={message.comment_id}
-                commentText={message.comment_fragment}
-                commentDateTime={message.datetime_created}
-                objectTitle={message.comment_obj_title}
-                commentee={message.commentee_username}
-            />;
+            return (
+                <CommentMessage
+                    actorId={message.actor_id}
+                    actorUsername={message.actor_username}
+                    className={className}
+                    commentDateTime={message.datetime_created}
+                    commentId={message.comment_id}
+                    commentText={message.comment_fragment}
+                    commentee={message.commentee_username}
+                    key={key}
+                    objectId={message.comment_obj_id}
+                    objectTitle={message.comment_obj_title}
+                    objectType={message.comment_type}
+                />
+            );
         case 'curatorinvite':
-            return <CuratorInviteMessage
-                key={key}
-                className={className}
-                actorUsername={message.actor_username}
-                studioId={message.gallery_id}
-                studioTitle={message.title}
-                datetimePromoted={message.datetime_created}
-            />;
+            return (
+                <CuratorInviteMessage
+                    actorUsername={message.actor_username}
+                    className={className}
+                    datetimePromoted={message.datetime_created}
+                    key={key}
+                    studioId={message.gallery_id}
+                    studioTitle={message.title}
+                />
+            );
         case 'remixproject':
-            return <RemixProjectMessage
-                key={key}
-                className={className}
-                actorUsername={message.actor_username}
-                projectId={message.project_id}
-                projectTitle={message.title}
-                parentId={message.parent_id}
-                parentTitle={message.parent_title}
-                remixDate={message.datetime_created}
-            />;
+            return (
+                <RemixProjectMessage
+                    actorUsername={message.actor_username}
+                    className={className}
+                    key={key}
+                    parentId={message.parent_id}
+                    parentTitle={message.parent_title}
+                    projectId={message.project_id}
+                    projectTitle={message.title}
+                    remixDate={message.datetime_created}
+                />
+            );
         case 'studioactivity':
-            return <StudioActivityMessage
-                key={key}
-                className={className}
-                studioId={message.gallery_id}
-                studioTitle={message.title}
-                datetimeCreated={message.datetime_created}
-            />;
+            return (
+                <StudioActivityMessage
+                    className={className}
+                    datetimeCreated={message.datetime_created}
+                    key={key}
+                    studioId={message.gallery_id}
+                    studioTitle={message.title}
+                />
+            );
         case 'forumpost':
-            return <ForumPostMessage
-                key={key}
-                className={className}
-                actorUsername={message.actor_username}
-                topicId={message.topic_id}
-                topicTitle={message.topic_title}
-                datetimeCreated={message.datetime_created}
-            />;
+            return (
+                <ForumPostMessage
+                    actorUsername={message.actor_username}
+                    className={className}
+                    datetimeCreated={message.datetime_created}
+                    key={key}
+                    topicId={message.topic_id}
+                    topicTitle={message.topic_title}
+                />
+            );
         case 'becomeownerstudio':
-            return <BecomeManagerMessage
-                key={key}
-                className={className}
-                actorUsername={message.actor_username}
-                studioId={message.gallery_id}
-                studioTitle={message.gallery_title}
-                datetimePromoted={message.datetime_created}
-            />;
+            return (
+                <BecomeManagerMessage
+                    actorUsername={message.actor_username}
+                    className={className}
+                    datetimePromoted={message.datetime_created}
+                    key={key}
+                    studioId={message.gallery_id}
+                    studioTitle={message.gallery_title}
+                />
+            );
         case 'userjoin':
-            return <UserJoinMessage
-                key={key}
-                className={className}
-                datetimeJoined={message.datetime_created}
-            />;
+            return (
+                <UserJoinMessage
+                    className={className}
+                    datetimeJoined={message.datetime_created}
+                    key={key}
+                />
+            );
         }
-    },
-    renderSocialMessages: function (messages, unreadCount) {
-        var messageList = [];
-        for (var i in messages) {
-            if (i <= unreadCount) {
-                messageList.push(this.getComponentForMessage(messages[i], true));
+    }
+    renderSocialMessages (messages, unreadCount) {
+        const messageList = [];
+        let counter = 0;
+        for (const message of messages) {
+            if (counter <= unreadCount) {
+                messageList.push(this.getComponentForMessage(message, true));
             } else {
-                messageList.push(this.getComponentForMessage(messages[i], false));
+                messageList.push(this.getComponentForMessage(message, false));
             }
+            counter++;
         }
         return messageList;
-    },
-    renderLoadMore: function (loadMore) {
+    }
+    renderLoadMore (loadMore) {
         if (loadMore) {
-            return <Button
-                onClick={this.props.loadMoreMethod}
-                className="messages-social-loadmore white"
-                key="load-more"
-            >
-                <FormattedMessage id='general.loadMore' />
-            </Button>;
+            return (
+                <Button
+                    className="messages-social-loadmore white"
+                    key="load-more"
+                    onClick={this.props.onLoadMoreMethod}
+                >
+                    <FormattedMessage id="general.loadMore" />
+                </Button>
+            );
         }
         return null;
-    },
-    renderMessageCounter: function (numNewMessages) {
+    }
+    renderMessageCounter (numNewMessages) {
         if (numNewMessages > 0) {
-            return <div className="messages-header-unread">
-                <FormattedNumber value={numNewMessages} />
-            </div>;
+            return (
+                <div className="messages-header-unread">
+                    <FormattedNumber value={numNewMessages} />
+                </div>
+            );
         }
         return null;
-    },
-    render: function () {
+    }
+    render () {
         if (this.props.loadStatus === messageStatuses.MESSAGES_ERROR) {
             return (
                 <section className="messages-social">
                     <div className="messages-social-title">
                         <h4>
-                            <FormattedMessage id='messages.messageTitle' />
+                            <FormattedMessage id="messages.messageTitle" />
                         </h4>
                     </div>
-                    <p><FormattedMessage id='messages.requestError' /></p>
+                    <p><FormattedMessage id="messages.requestError" /></p>
                 </section>
             );
         }
@@ -188,13 +212,19 @@ var SocialMessagesList = React.createClass({
         return (
             <section className="messages-social">
                 {this.props.messages.length > 0 ? [
-                    <div className="messages-social-title" key="messages-social-title">
+                    <div
+                        className="messages-social-title"
+                        key="messages-social-title"
+                    >
                         <h4 className="messages-header">
-                            <FormattedMessage id='messages.messageTitle' />
+                            <FormattedMessage id="messages.messageTitle" />
                             {this.renderMessageCounter(this.props.numNewMessages)}
                         </h4>
                     </div>,
-                    <ul className="messages-social-list" key="messages-social-list">
+                    <ul
+                        className="messages-social-list"
+                        key="messages-social-list"
+                    >
                         {this.renderSocialMessages(this.props.messages, (this.props.numNewMessages - 1))}
                     </ul>
                 ] : []}
@@ -202,137 +232,168 @@ var SocialMessagesList = React.createClass({
             </section>
         );
     }
-});
+}
 
-var MessagesPresentation = injectIntl(React.createClass({
-    type: 'MessagesPresentation',
-    propTypes: {
-        sessionStatus: React.PropTypes.string.isRequired,
-        user: React.PropTypes.object.isRequired,
-        messages: React.PropTypes.array.isRequired,
-        adminMessages: React.PropTypes.array.isRequired,
-        scratcherInvite: React.PropTypes.object.isRequired,
-        numNewMessages: React.PropTypes.number,
-        handleFilterClick: React.PropTypes.func.isRequired,
-        handleAdminDismiss: React.PropTypes.func.isRequired,
-        loadMore: React.PropTypes.bool.isRequired,
-        loadMoreMethod: React.PropTypes.func,
-        requestStatus: React.PropTypes.object.isRequired,
-        filter: React.PropTypes.string
-    },
-    getDefaultProps: function () {
-        return {
-            numNewMessages: 0,
-            filterOpen: false,
-            filter: ''
-        };
-    },
-    render: function () {
-        var adminMessageLength = this.props.adminMessages.length;
-        if (Object.keys(this.props.scratcherInvite).length > 0) {
-            adminMessageLength = adminMessageLength + 1;
-        }
-        var numNewSocialMessages = this.props.numNewMessages - adminMessageLength;
-        if (numNewSocialMessages < 0) {
-            numNewSocialMessages = 0;
-        }
+SocialMessagesList.propTypes = {
+    loadMore: PropTypes.bool.isRequired,
+    loadStatus: PropTypes.string,
+    messages: PropTypes.arrayOf(PropTypes.object).isRequired,
+    numNewMessages: PropTypes.number,
+    onLoadMoreMethod: PropTypes.func
+};
 
-        return (
-            <div className="messages">
-                <TitleBanner className="mod-messages">
-                    <FlexRow className="inner mod-messages-title">
-                        <h1 className="title-banner-h1 mod-messages">
-                            <FormattedMessage id='messages.messageTitle' />
-                        </h1>
-                        <div className="messages-title-filter">
-                            <Form>
-                                <Select
-                                    label={this.props.intl.formatMessage({id: 'messages.filterBy'})}
-                                    name="messages.filter"
-                                    onChange={this.props.handleFilterClick}
-                                    options={[
-                                        {
-                                            label: this.props.intl.formatMessage({id: 'messages.activityAll'}),
-                                            value: ''
-                                        },
-                                        {
-                                            label: this.props.intl.formatMessage({id: 'messages.activityComments'}),
-                                            value: 'comments'
-                                        },
-                                        {
-                                            label: this.props.intl.formatMessage({id: 'messages.activityProjects'}),
-                                            value: 'projects'
-                                        },
-                                        {
-                                            label: this.props.intl.formatMessage({id: 'messages.activityStudios'}),
-                                            value: 'studios'
-                                        },
-                                        {
-                                            label: this.props.intl.formatMessage({id: 'messages.activityForums'}),
-                                            value: 'forums'
-                                        }
-                                    ]}
-                                    value={this.props.filter}
-                                />
-                            </Form>
-                        </div>
-                    </FlexRow>
-                </TitleBanner>
-                <div className="messages-details inner">
-                    {this.props.adminMessages.length > 0 || Object.keys(this.props.scratcherInvite).length > 0 ? [
-                        <section className="messages-admin">
-                            <div className="messages-admin-title">
-                                <h4 className="messages-header">
-                                    <FormattedMessage id='messages.scratchTeamTitle' />
-                                    <div className="messages-header-unread">
-                                        <FormattedNumber value={adminMessageLength} />
-                                    </div>
-                                </h4>
-                            </div>
-                            <ul className="messages-admin-list">
-                                {Object.keys(this.props.scratcherInvite).length > 0 ? [
-                                    <ScratcherInvite
-                                        id={this.props.scratcherInvite.id}
-                                        username={this.props.user.username}
-                                        datetimeCreated={this.props.scratcherInvite.datetime_created}
-                                        onDismiss={function () {
-                                            this.props.handleAdminDismiss('invite', this.props.scratcherInvite.id);
-                                        }.bind(this)}
-                                    />
-                                ] : []}
-                                {this.props.adminMessages.map(function (item) {
-                                    return <AdminMessage
-                                        key={'adminmessage' + item.id}
-                                        id={item.id}
-                                        message={item.message}
-                                        datetimeCreated={item.datetime_created}
-                                        onDismiss={function () {
-                                            this.props.handleAdminDismiss('notification', item.id);
-                                        }.bind(this)}
-                                    />;
-                                }.bind(this))}
-                            </ul>
-                        </section>
-                    ] : []}
-                    {this.props.requestStatus.admin === messageStatuses.ADMIN_ERROR ? [
-                        <section className="messages-admin">
-                            <h4>
-                                <FormattedMessage id='messages.scratchTeamTitle' />
-                            </h4>
-                            <p><FormattedMessage id='messages.requestError' /></p>
-                        </section>
-                    ] : []}
-                    <SocialMessagesList
-                        loadStatus={this.props.requestStatus.messages}
-                        messages={this.props.messages}
-                        numNewMessages={numNewSocialMessages}
-                        loadMore={this.props.loadMore}
-                        loadMoreMethod={this.props.loadMoreMethod}
-                    />
-                </div>
-            </div>
-        );
+SocialMessagesList.defaultProps = {
+    loadStatus: messageStatuses.FETCHING,
+    numNewMessages: 0
+};
+
+const MessagesPresentation = props => {
+    let adminMessageLength = props.adminMessages.length;
+    if (Object.keys(props.scratcherInvite).length > 0) {
+        adminMessageLength = adminMessageLength + 1;
+    }
+    let numNewSocialMessages = props.numNewMessages - adminMessageLength;
+    if (numNewSocialMessages < 0) {
+        numNewSocialMessages = 0;
     }
-}));
 
-module.exports = MessagesPresentation;
+    return (
+        <div className="messages">
+            <TitleBanner className="mod-messages">
+                <FlexRow className="inner mod-messages-title">
+                    <h1 className="title-banner-h1 mod-messages">
+                        <FormattedMessage id="messages.messageTitle" />
+                    </h1>
+                    <div className="messages-title-filter">
+                        <Form>
+                            <Select
+                                label={props.intl.formatMessage({id: 'messages.filterBy'})}
+                                name="messages.filter"
+                                options={[
+                                    {
+                                        label: props.intl.formatMessage({id: 'messages.activityAll'}),
+                                        value: ''
+                                    },
+                                    {
+                                        label: props.intl.formatMessage({id: 'messages.activityComments'}),
+                                        value: 'comments'
+                                    },
+                                    {
+                                        label: props.intl.formatMessage({id: 'messages.activityProjects'}),
+                                        value: 'projects'
+                                    },
+                                    {
+                                        label: props.intl.formatMessage({id: 'messages.activityStudios'}),
+                                        value: 'studios'
+                                    },
+                                    {
+                                        label: props.intl.formatMessage({id: 'messages.activityForums'}),
+                                        value: 'forums'
+                                    }
+                                ]}
+                                value={props.filter}
+                                onChange={props.onFilterClick}
+                            />
+                        </Form>
+                    </div>
+                </FlexRow>
+            </TitleBanner>
+            <div className="messages-details inner">
+                {props.adminMessages.length > 0 || Object.keys(props.scratcherInvite).length > 0 ? [
+                    <section
+                        className="messages-admin"
+                        key="messages-admin"
+                    >
+                        <div className="messages-admin-title">
+                            <h4 className="messages-header">
+                                <FormattedMessage id="messages.scratchTeamTitle" />
+                                <div className="messages-header-unread">
+                                    <FormattedNumber value={adminMessageLength} />
+                                </div>
+                            </h4>
+                        </div>
+                        <ul className="messages-admin-list">
+                            {Object.keys(props.scratcherInvite).length > 0 ? [
+                                <ScratcherInvite
+                                    datetimeCreated={props.scratcherInvite.datetime_created}
+                                    id={props.scratcherInvite.id}
+                                    key={`invite${props.scratcherInvite.id}`}
+                                    username={props.user.username}
+                                    onDismiss={() => { // eslint-disable-line react/jsx-no-bind
+                                        props.onAdminDismiss('invite', props.scratcherInvite.id);
+                                    }}
+                                />
+                            ] : []}
+                            {props.adminMessages.map(item => (
+                                <AdminMessage
+                                    datetimeCreated={item.datetime_created}
+                                    id={item.id}
+                                    key={`adminmessage${item.id}`}
+                                    message={item.message}
+                                    onDismiss={() => { // eslint-disable-line react/jsx-no-bind
+                                        props.onAdminDismiss('notification', item.id);
+                                    }}
+                                />
+                            ))}
+                        </ul>
+                    </section>
+                ] : []}
+                {props.requestStatus.admin === messageStatuses.ADMIN_ERROR ? [
+                    <section
+                        className="messages-admin"
+                        key="messages-admin-error"
+                    >
+                        <h4>
+                            <FormattedMessage id="messages.scratchTeamTitle" />
+                        </h4>
+                        <p><FormattedMessage id="messages.requestError" /></p>
+                    </section>
+                ] : []}
+                <SocialMessagesList
+                    loadMore={props.loadMore}
+                    loadStatus={props.requestStatus.message}
+                    messages={props.messages}
+                    numNewMessages={numNewSocialMessages}
+                    onLoadMoreMethod={props.onLoadMoreMethod}
+                />
+            </div>
+        </div>
+    );
+};
+
+MessagesPresentation.propTypes = {
+    adminMessages: PropTypes.arrayOf(PropTypes.object).isRequired,
+    filter: PropTypes.string,
+    intl: intlShape,
+    loadMore: PropTypes.bool.isRequired,
+    messages: PropTypes.arrayOf(PropTypes.object).isRequired,
+    numNewMessages: PropTypes.number,
+    onAdminDismiss: PropTypes.func.isRequired,
+    onFilterClick: PropTypes.func.isRequired,
+    onLoadMoreMethod: PropTypes.func,
+    requestStatus: PropTypes.shape({
+        admin: PropTypes.string,
+        clear: PropTypes.string,
+        message: PropTypes.string,
+        delete: PropTypes.string
+    }).isRequired,
+    scratcherInvite: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
+    user: PropTypes.shape({
+        id: PropTypes.number,
+        banned: PropTypes.bool,
+        username: PropTypes.string,
+        token: PropTypes.string,
+        thumbnailUrl: PropTypes.string,
+        dateJoined: PropTypes.string,
+        email: PropTypes.string,
+        classroomId: PropTypes.string
+    }).isRequired
+};
+
+MessagesPresentation.defaultProps = {
+    filter: '',
+    filterOpen: false,
+    numNewMessages: 0
+};
+
+module.exports = injectIntl(MessagesPresentation);
diff --git a/src/views/microworld/art/art.jsx b/src/views/microworld/art/art.jsx
index 62678a605..d70d60e48 100644
--- a/src/views/microworld/art/art.jsx
+++ b/src/views/microworld/art/art.jsx
@@ -1,8 +1,10 @@
-var React = require('react'); // eslint-disable-line
-var render = require('../../../lib/render.jsx');
-var Microworld = require('../../../components/microworld/microworld.jsx');
-var Page = require('../../../components/page/www/page.jsx');
+const React = require('react'); // eslint-disable-line
 
-var microworldData = require('./art.json');
+const Microworld = require('../../../components/microworld/microworld.jsx');
+
+const Page = require('../../../components/page/www/page.jsx');
+const render = require('../../../lib/render.jsx');
+
+const microworldData = require('./art.json');
 
 render(<Page><Microworld microworldData={microworldData} /></Page>, document.getElementById('app'));
diff --git a/src/views/microworld/fashion/fashion.jsx b/src/views/microworld/fashion/fashion.jsx
index 0097fb9ad..17d37b470 100644
--- a/src/views/microworld/fashion/fashion.jsx
+++ b/src/views/microworld/fashion/fashion.jsx
@@ -1,8 +1,10 @@
-var React = require('react'); // eslint-disable-line
-var render = require('../../../lib/render.jsx');
-var Microworld = require('../../../components/microworld/microworld.jsx');
-var Page = require('../../../components/page/www/page.jsx');
+const React = require('react'); // eslint-disable-line
 
-var microworldData = require('./fashion.json');
+const Microworld = require('../../../components/microworld/microworld.jsx');
+
+const Page = require('../../../components/page/www/page.jsx');
+const render = require('../../../lib/render.jsx');
+
+const microworldData = require('./fashion.json');
 
 render(<Page><Microworld microworldData={microworldData} /></Page>, document.getElementById('app'));
diff --git a/src/views/microworld/hiphop/hiphop.jsx b/src/views/microworld/hiphop/hiphop.jsx
index ed4ce62aa..0408aae29 100644
--- a/src/views/microworld/hiphop/hiphop.jsx
+++ b/src/views/microworld/hiphop/hiphop.jsx
@@ -1,8 +1,10 @@
-var React = require('react'); // eslint-disable-line
-var render = require('../../../lib/render.jsx');
-var Microworld = require('../../../components/microworld/microworld.jsx');
-var Page = require('../../../components/page/www/page.jsx');
+const React = require('react'); // eslint-disable-line
 
-var microworldData = require('./hiphop.json');
+const Microworld = require('../../../components/microworld/microworld.jsx');
+
+const Page = require('../../../components/page/www/page.jsx');
+const render = require('../../../lib/render.jsx');
+
+const microworldData = require('./hiphop.json');
 
 render(<Page><Microworld microworldData={microworldData} /></Page>, document.getElementById('app'));
diff --git a/src/views/microworld/soccer/soccer.jsx b/src/views/microworld/soccer/soccer.jsx
index 0aada133b..5b71c1e78 100644
--- a/src/views/microworld/soccer/soccer.jsx
+++ b/src/views/microworld/soccer/soccer.jsx
@@ -1,8 +1,10 @@
-var React = require('react'); // eslint-disable-line
-var render = require('../../../lib/render.jsx');
-var Microworld = require('../../../components/microworld/microworld.jsx');
-var Page = require('../../../components/page/www/page.jsx');
+const React = require('react'); // eslint-disable-line
 
-var microworldData = require('./soccer.json');
+const Microworld = require('../../../components/microworld/microworld.jsx');
+
+const Page = require('../../../components/page/www/page.jsx');
+const render = require('../../../lib/render.jsx');
+
+const microworldData = require('./soccer.json');
 
 render(<Page><Microworld microworldData={microworldData} /></Page>, document.getElementById('app'));
diff --git a/src/views/microworldshomepage/microworldshomepage.jsx b/src/views/microworldshomepage/microworldshomepage.jsx
index f68520ac6..8c2cb4a33 100644
--- a/src/views/microworldshomepage/microworldshomepage.jsx
+++ b/src/views/microworldshomepage/microworldshomepage.jsx
@@ -1,47 +1,49 @@
-var React = require('react');
-var injectIntl = require('react-intl').injectIntl;
-var FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
-var FormattedMessage = require('react-intl').FormattedMessage;
-var render = require('../../lib/render.jsx');
+const injectIntl = require('react-intl').injectIntl;
+const intlShape = require('react-intl').intlShape;
+const FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
+const FormattedMessage = require('react-intl').FormattedMessage;
+const React = require('react');
 
-var MasonryGrid = require('../../components/masonrygrid/masonrygrid.jsx');
-var Page = require('../../components/page/www/page.jsx');
-var TitleBanner = require('../../components/title-banner/title-banner.jsx');
-var TTTTile = require('../../components/ttt-tile/ttt-tile.jsx');
-var Tiles = require('./microworlds.json');
+const MasonryGrid = require('../../components/masonrygrid/masonrygrid.jsx');
+const TitleBanner = require('../../components/title-banner/title-banner.jsx');
+const TTTTile = require('../../components/ttt-tile/ttt-tile.jsx');
+
+const Page = require('../../components/page/www/page.jsx');
+const render = require('../../lib/render.jsx');
+
+const Tiles = require('./microworlds.json');
 
 require('./microworldshomepage.scss');
 
-var MicroworldsHomepage = injectIntl(React.createClass({
-    type: 'MicroworldsHomepage',
-    render: function () {
-        return (
-            <div className="microworlds">
-                <TitleBanner className="masthead mod-blue-bg">
-                    <h1 className="title-banner-h1">
-                        <FormattedMessage id="microworlds.title" />
-                    </h1>
-                    <p className="intro title-banner-p">
-                        <FormattedHTMLMessage id="microworlds.subTitle" />
-                    </p>
-                </TitleBanner>
-                <div className="inner">
-                    <MasonryGrid >
-                        {Tiles.map(
-                            function (tile, key) {
-                                return <TTTTile
-                                    key={key}
-                                    title={this.props.intl.formatMessage({id: tile.title})}
-                                    tutorialLoc={tile.tutorialLoc}
-                                    thumbUrl={tile.thumbUrl}
-                                    />;
-                            }, this)
-                        }
-                    </MasonryGrid>
-                </div>
-            </div>
-        );
-    }
-}));
+const MicroworldsHomepage = props => (
+    <div className="microworlds">
+        <TitleBanner className="masthead mod-blue-bg">
+            <h1 className="title-banner-h1">
+                <FormattedMessage id="microworlds.title" />
+            </h1>
+            <p className="intro title-banner-p">
+                <FormattedHTMLMessage id="microworlds.subTitle" />
+            </p>
+        </TitleBanner>
+        <div className="inner">
+            <MasonryGrid >
+                {Tiles.map((tile, key) => (
+                    <TTTTile
+                        key={key}
+                        thumbUrl={tile.thumbUrl}
+                        title={props.intl.formatMessage({id: tile.title})}
+                        tutorialLoc={tile.tutorialLoc}
+                    />
+                ))}
+            </MasonryGrid>
+        </div>
+    </div>
+);
 
-render(<Page><MicroworldsHomepage /></Page>, document.getElementById('app'));
+MicroworldsHomepage.propTypes = {
+    intl: intlShape
+};
+
+const WrappedMicroworldsHomepage = injectIntl(MicroworldsHomepage);
+
+render(<Page><WrappedMicroworldsHomepage /></Page>, document.getElementById('app'));
diff --git a/src/views/preview-faq/preview-faq.jsx b/src/views/preview-faq/preview-faq.jsx
index d054ec5ae..d272ee327 100644
--- a/src/views/preview-faq/preview-faq.jsx
+++ b/src/views/preview-faq/preview-faq.jsx
@@ -1,42 +1,50 @@
-var cheerio = require('cheerio');
-var injectIntl = require('react-intl').injectIntl;
-var React = require('react');
-var xhr = require('xhr');
+const cheerio = require('cheerio');
+const injectIntl = require('react-intl').injectIntl;
+const intlShape = require('react-intl').intlShape;
+const React = require('react');
+const xhr = require('xhr');
 
-var InformationPage = require('../../components/informationpage/informationpage.jsx');
-var Page = require('../../components/page/www/page.jsx');
-var render = require('../../lib/render.jsx');
+const InformationPage = require('../../components/informationpage/informationpage.jsx');
+
+const Page = require('../../components/page/www/page.jsx');
+const render = require('../../lib/render.jsx');
 
 require('./preview-faq.scss');
 
-var PreviewFaq = injectIntl(React.createClass({
-    type: 'PreviewFaq',
-    getInitialState: function () {
-        return {
+class PreviewFaq extends React.Component {
+    constructor (props) {
+        super(props);
+        this.state = {
             faqDoc: {__html: ''}
         };
-    },
-    componentDidMount: function () {
+    }
+    componentDidMount () {
         xhr({
             method: 'GET',
             uri: 'https://docs.google.com/document/d/e/2PACX-1vQZFrpOagYqEwcrBBCplIomiyguPAodIJVnCq9Sr11WDI_aa2b-JtDWak-Aiu-cwWobTXftRMF6wBbd/pub?embedded=true'
-        }, function (error, response, body) {
-            var $ = cheerio.load(body);
+        }, (error, response, body) => {
+            const $ = cheerio.load(body);
             this.setState({faqDoc: {__html: $('html').html()}});
-        }.bind(this));
-    },
-    render: function () {
+        });
+    }
+    render () {
         return (
             <InformationPage title={this.props.intl.formatMessage({id: 'preview-faq.title'})}>
                 <div className="inner">
                     <div
                         className="preview-faq"
-                        dangerouslySetInnerHTML={this.state.faqDoc}
+                        dangerouslySetInnerHTML={this.state.faqDoc} // eslint-disable-line react/no-danger
                     />
                 </div>
             </InformationPage>
         );
     }
-}));
+}
 
-render(<Page><PreviewFaq /></Page>, document.getElementById('app'));
+PreviewFaq.propTypes = {
+    intl: intlShape
+};
+
+const WrappedPreviewFAQ = injectIntl(PreviewFaq);
+
+render(<Page><WrappedPreviewFAQ /></Page>, document.getElementById('app'));
diff --git a/src/views/privacypolicy/privacypolicy.jsx b/src/views/privacypolicy/privacypolicy.jsx
index 69ab4eac7..a7b7182f0 100644
--- a/src/views/privacypolicy/privacypolicy.jsx
+++ b/src/views/privacypolicy/privacypolicy.jsx
@@ -1,228 +1,224 @@
-var React = require('react');
-var render = require('../../lib/render.jsx');
+const React = require('react');
 
-var Page = require('../../components/page/www/page.jsx');
-var InformationPage = require('../../components/informationpage/informationpage.jsx');
+const InformationPage = require('../../components/informationpage/informationpage.jsx');
 
-var Privacypolicy = React.createClass({
-    type: 'Privacypolicy',
-    render: function () {
-        return (
-            <InformationPage title={'Privacy Policy'}>
-                <div className="inner info-inner">
-                    <section>
-                        <p className="intro">
-                            We made Scratch so people like you could create projects,
-                             share ideas, and build a community. To make this happen,
-                             we collect some information for our users. The Scratch Team
-                             understands how important privacy is to our community,
-                             especially kids and parents. We wrote this privacy policy
-                             to explain what information we collect, how we use it,
-                             and what we're doing to keep it safe. If you have any
-                             questions regarding this privacy policy, you can{' '}
-                             <a href="/contact-us">contact us</a>.
-                        </p>
-                        <p className="callout">
-                            Please do not share personal contact information, such as
-                             your name, physical address, email address, phone number,
-                             or anything else that can be used to make contact outside
-                             of the Scratch website. Please report projects, comments,
-                             or forum posts that contain this kind of information so
-                             the Scratch team can remove it, and please remind the
-                             author of our policy.
-                         </p>
-                    </section>
-                    <section id="collection">
-                        <dl>
-                            <span className="nav-spacer"></span>
-                            <h3>What information does the Scratch Team collect about me?</h3>
-                            <dt>Account Information: </dt>
-                            <dd>
-                                In order to build projects or comment on other users' projects,
-                                 you need to make an account. During account creation, we ask you
-                                 for a username, your country, birth month and year, gender, and
-                                 your email address (or your parent or guardian's email address if
-                                 you are under 13 years old). We ask that you select a user name
-                                 that does not disclose your real name or other information that
-                                 could identify you. Other users can see your username and country,
-                                 but not your age, gender, or email address.
-                            </dd>
-                            <dt>User-generated Content: </dt>
-                            <dd>
-                                All of your Scratch projects, comments, and forum posts are stored
-                                 on the Scratch servers. Other users can see your shared projects,
-                                 comments, and forum posts, along with your username. Because the
-                                 Scratch Team is responsible for moderation, we have access to all
-                                 content stored on the Scratch website, including unshared projects.
-                                 If you prefer to work on projects in complete privacy, you can use
-                                 either the <a href="/download">Scratch 2 offline editor</a>{' '}
-                                 or <a href="/scratch_1.4">Scratch 1.4</a>.
-                            </dd>
-                            <dt>Usage Information: </dt>
-                            <dd>
-                                When you use Scratch, our servers will automatically store a limited
-                                 amount of information about how you use the website. This information
-                                 includes a number that identifies your computer (the IP address), which
-                                 pages you visited, and what browser you are using.
-                            </dd>
-                            <dt>Google Analytics: </dt>
-                            <dd>
-                                We also collect some data on where you click and which parts of the
-                                 site you visit using Google Analytics. This "click data" helps us figure
-                                 out ways to improve the website. Information collected and processed by
-                                 Google Analytics includes the user's IP address, network location, and
-                                 geographic location. Google Analytics acquires all its information
-                                 directly from the user, by installing a cookie (see below) on your
-                                 computer, if you have enabled JavaScript. Scratch does not share any
-                                 information it collects with Google, and Google does not collect any
-                                 personal identifying information about you.
-                            </dd>
-                            <dt>Cookies: </dt>
-                            <dd>
-                                When you log in, the Scratch website asks your browser to put an http
-                                 "cookie" on your computer. The cookie is actually a small text file
-                                 that our site can send to your browser for storage on your computer.
-                                 This allows the website to remember that you are logged in when you
-                                 go to a different page.
-                            </dd>
-                        </dl>
-                    </section>
-                    <section id="usage">
-                        <span className="nav-spacer"></span>
-                        <h3>How does the Scratch Team use my information?</h3>
+const Page = require('../../components/page/www/page.jsx');
+const render = require('../../lib/render.jsx');
+
+const Privacypolicy = () => (
+    <InformationPage title={'Privacy Policy'}>
+        <div className="inner info-inner">
+            <section>
+                <p className="intro">
+                    We made Scratch so people like you could create projects,
+                    share ideas, and build a community. To make this happen,
+                    we collect some information for our users. The Scratch Team
+                    understands how important privacy is to our community,
+                    especially kids and parents. We wrote this privacy policy
+                    to explain what information we collect, how we use it,
+                    and what we&#39;re doing to keep it safe. If you have any
+                    questions regarding this privacy policy, you can{' '}
+                    <a href="/contact-us">contact us</a>.
+                </p>
+                <p className="callout">
+                    Please do not share personal contact information, such as
+                    your name, physical address, email address, phone number,
+                    or anything else that can be used to make contact outside
+                    of the Scratch website. Please report projects, comments,
+                    or forum posts that contain this kind of information so
+                    the Scratch team can remove it, and please remind the
+                    author of our policy.
+                </p>
+            </section>
+            <section id="collection">
+                <dl>
+                    <span className="nav-spacer" />
+                    <h3>What information does the Scratch Team collect about me?</h3>
+                    <dt>Account Information: </dt>
+                    <dd>
+                        In order to build projects or comment on other users&#39; projects,
+                        you need to make an account. During account creation, we ask you
+                        for a username, your country, birth month and year, gender, and
+                        your email address (or your parent or guardian&#39;s email address if
+                        you are under 13 years old). We ask that you select a user name
+                        that does not disclose your real name or other information that
+                        could identify you. Other users can see your username and country,
+                        but not your age, gender, or email address.
+                    </dd>
+                    <dt>User-generated Content: </dt>
+                    <dd>
+                        All of your Scratch projects, comments, and forum posts are stored
+                        on the Scratch servers. Other users can see your shared projects,
+                        comments, and forum posts, along with your username. Because the
+                        Scratch Team is responsible for moderation, we have access to all
+                        content stored on the Scratch website, including unshared projects.
+                        If you prefer to work on projects in complete privacy, you can use
+                        either the <a href="/download">Scratch 2 offline editor</a>{' '}
+                        or <a href="/scratch_1.4">Scratch 1.4</a>.
+                    </dd>
+                    <dt>Usage Information: </dt>
+                    <dd>
+                        When you use Scratch, our servers will automatically store a limited
+                         amount of information about how you use the website. This information
+                         includes a number that identifies your computer (the IP address), which
+                         pages you visited, and what browser you are using.
+                    </dd>
+                    <dt>Google Analytics: </dt>
+                    <dd>
+                        We also collect some data on where you click and which parts of the
+                        site you visit using Google Analytics. This &#34;click data&#34; helps us figure
+                        out ways to improve the website. Information collected and processed by
+                        Google Analytics includes the user&#39;s IP address, network location, and
+                        geographic location. Google Analytics acquires all its information
+                        directly from the user, by installing a cookie (see below) on your
+                        computer, if you have enabled JavaScript. Scratch does not share any
+                        information it collects with Google, and Google does not collect any
+                        personal identifying information about you.
+                    </dd>
+                    <dt>Cookies: </dt>
+                    <dd>
+                        When you log in, the Scratch website asks your browser to put an http
+                        &#34;cookie&#34; on your computer. The cookie is actually a small text file
+                        that our site can send to your browser for storage on your computer.
+                        This allows the website to remember that you are logged in when you
+                        go to a different page.
+                    </dd>
+                </dl>
+            </section>
+            <section id="usage">
+                <span className="nav-spacer" />
+                <h3>How does the Scratch Team use my information?</h3>
+                <ul>
+                    <li>
+                        We collect age and gender data so that we know who is using our
+                         website.
+                    </li>
+                    <li>
+                        If you forget your password, we will ask you to disclose to us
+                         your birth month and year so that we can verify your account, and
+                         your email address so that we can send you a new password.
+                    </li>
+                    <li>
+                        We will use your email address to respond to messages you send us
+                         or to communicate with you about the Scratch service or your account.
+                    </li>
+                    <li>
+                        We send out occasional email updates about Scratch to the confirmed
+                         email address on your account. Scratch will never sell or share your
+                         email address without your permission. You can unsubscribe from these
+                         updates by clicking the unsubscribe link found at the bottom of the
+                         email.
+                    </li>
+                    <li>
+                        Parents and guardians who register their under-13 year olds for
+                        Scratch may also receive additional updates from the{' '}
+                        <a href="http://www.scratchfoundation.org/">Scratch Foundation</a>,
+                        a non-profit that supports Scratch educational initiatives.
+                        The Scratch Foundation will never sell or share your email
+                        address without your permission. You can unsubscribe from these
+                        updates by clicking the unsubscribe link found at the bottom of
+                        the email.
+                    </li>
+                    <li>
+                        If we detect repeated abusive behavior from your account, IP address,
+                        or email address, we may share your account name, IP address, and the
+                        time and content of the abusive behavior with the IP address owner
+                        (such as a school or internet service provider).
+                    </li>
+                    <li>
+                        We may use de-identified location, age, gender, and usage data
+                        in research studies intended to improve our understanding of
+                        how people learn with Scratch. The results of this research are
+                        shared with educators and researchers through conferences,
+                        journals, and other publications. You can find out more on our{' '}
+                        <a href="/info/research">Research page</a>.
+                    </li>
+                    <li>
+                        We may disclose some of the information we collect to third-party
+                        service providers that help us manage communications to and from
+                        the Scratch website and improve website performance. We are
+                        satisfied that these service providers have privacy policies that
+                        restrict them from further disclosing any of your information.
+                    </li>
+                    <li>
+                        Other than as described above, we will never share personally
+                        identifiable information about you with any other person,
+                        company, or organization, except:
                         <ul>
                             <li>
-                                We collect age and gender data so that we know who is using our
-                                 website.
+                                As required to comply with our obligations under the law.
                             </li>
                             <li>
-                                If you forget your password, we will ask you to disclose to us
-                                 your birth month and year so that we can verify your account, and
-                                 your email address so that we can send you a new password.
-                            </li>
-                            <li>
-                                We will use your email address to respond to messages you send us
-                                 or to communicate with you about the Scratch service or your account.
-                            </li>
-                            <li>
-                                We send out occasional email updates about Scratch to the confirmed
-                                 email address on your account. Scratch will never sell or share your
-                                 email address without your permission. You can unsubscribe from these
-                                 updates by clicking the unsubscribe link found at the bottom of the
-                                 email.
-                            </li>
-                            <li>
-                                Parents and guardians who register their under-13 year olds for
-                                 Scratch may also receive additional updates from the{' '}
-                                 <a href="http://www.scratchfoundation.org/">Scratch Foundation</a>,
-                                 a non-profit that supports Scratch educational initiatives.
-                                 The Scratch Foundation will never sell or share your email
-                                 address without your permission. You can unsubscribe from these
-                                 updates by clicking the unsubscribe link found at the bottom of
-                                 the email.
-                            </li>
-                            <li>
-                                If we detect repeated abusive behavior from your account, IP address,
-                                 or email address, we may share your account name, IP address, and the
-                                 time and content of the abusive behavior with the IP address owner
-                                 (such as a school or internet service provider).
-                            </li>
-                            <li>
-                                We may use de-identified location, age, gender, and usage data
-                                 in research studies intended to improve our understanding of
-                                 how people learn with Scratch. The results of this research are
-                                 shared with educators and researchers through conferences,
-                                 journals, and other publications. You can find out more on our{' '}
-                                 <a href="/info/research">Research page</a>.
-                            </li>
-                            <li>
-                                We may disclose some of the information we collect to third-party
-                                 service providers that help us manage communications to and from
-                                 the Scratch website and improve website performance. We are
-                                 satisfied that these service providers have privacy policies that
-                                 restrict them from further disclosing any of your information.
-                            </li>
-                            <li>
-                                Other than as described above, we will never share personally
-                                identifiable information about you with any other person,
-                                company, or organization, except:
-                                <ul>
-                                    <li>
-                                        As required to comply with our obligations under the law.
-                                    </li>
-                                    <li>
-                                        For technical reasons, if we are required to transfer the
-                                         data on our servers to another location or organization.
-                                    </li>
-                                </ul>
-                            </li>
-                            <li>
-                                Scratch reserves the right to share user information with the
-                                 appropriate authorities (including schools, school districts,
-                                 and law enforcement, when necessary) for the purposes of protecting
-                                 the safety of users, other individuals, or the security of the site.
+                                For technical reasons, if we are required to transfer the
+                                 data on our servers to another location or organization.
                             </li>
                         </ul>
-                    </section>
-                    <section id="update">
-                        <span className="nav-spacer"></span>
-                        <h3>How can I update my personal information?</h3>
-                        <p>
-                            You can update your password, email address, and country through
-                             the <a href="/account/password_change">Account Settings</a> page.
-                             You can also reset your password through the{' '}
-                              <a href="/account/password_reset">Account Reset</a>{' '}
-                             page. You cannot change your username, but you can make a new
-                             account and manually copy your projects to the new account.
-                        </p>
-                        <p>
-                            If you want to delete your account entirely, log in to Scratch,
-                             and then click your username in the top right-hand corner. Select
-                             "Account Settings," then click the "I want to delete my account"
-                             link at the bottom of the page.
-                        </p>
-                    </section>
-                    <section id="protection">
-                        <span className="nav-spacer"></span>
-                        <h3>How does the Scratch Team protect my information?</h3>
-                        <p>
-                            The Scratch Team has in place physical and electronic procedures
-                             to protect the information we collect on the Scratch website. We
-                             strictly limit individual access to the Scratch servers and the
-                             data we store on them. However, as effective as these measures
-                             are, no security system is impenetrable. We cannot completely
-                             guarantee the security of our database, nor can we guarantee that
-                             the information you supply will not be intercepted while being
-                             transmitted to us over the Internet.
-                        </p>
-                    </section>
-                    <section id="changes">
-                        <span className="nav-spacer"></span>
-                        <h3>Notifications of Changes to the Privacy Policy</h3>
-                        <p>
-                            We review our security measures and Privacy Policy on a periodic
-                             basis, and we may modify our policies as appropriate. We may also
-                             change or update our Privacy Policy if we add new services or
-                             features. If we make any changes to our privacy practices, we will
-                             amend this Privacy Policy accordingly and post the amended policy
-                             on the Scratch website. We encourage you to review our Privacy
-                             Policy on a regular basis.
-                        </p>
-                    </section>
-                    <p>The Scratch Privacy Policy was last updated: November 2016</p>
-                </div>
-                <nav>
-                    <ol>
-                        <li><a href="#collection">What information does the Scratch Team collect about me?</a></li>
-                        <li><a href="#usage">How does the Scratch Team use my information?</a></li>
-                        <li><a href="#update">How can I update my personal information?</a></li>
-                        <li><a href="#protection">How does the Scratch Team protect my information?</a></li>
-                        <li><a href="#changes">Notifications of Changes to the Privacy Policy</a></li>
-                    </ol>
-                </nav>
-            </InformationPage>
-        );
-    }
-});
+                    </li>
+                    <li>
+                        Scratch reserves the right to share user information with the
+                        appropriate authorities (including schools, school districts,
+                        and law enforcement, when necessary) for the purposes of protecting
+                        the safety of users, other individuals, or the security of the site.
+                    </li>
+                </ul>
+            </section>
+            <section id="update">
+                <span className="nav-spacer" />
+                <h3>How can I update my personal information?</h3>
+                <p>
+                    You can update your password, email address, and country through
+                    the <a href="/account/password_change">Account Settings</a> page.
+                    You can also reset your password through the{' '}
+                    <a href="/account/password_reset">Account Reset</a>{' '}
+                    page. You cannot change your username, but you can make a new
+                    account and manually copy your projects to the new account.
+                </p>
+                <p>
+                    If you want to delete your account entirely, log in to Scratch,
+                    and then click your username in the top right-hand corner. Select
+                    &#34;Account Settings,&#34; then click the &#34;I want to delete my account&#34;
+                    link at the bottom of the page.
+                </p>
+            </section>
+            <section id="protection">
+                <span className="nav-spacer" />
+                <h3>How does the Scratch Team protect my information?</h3>
+                <p>
+                    The Scratch Team has in place physical and electronic procedures
+                     to protect the information we collect on the Scratch website. We
+                     strictly limit individual access to the Scratch servers and the
+                     data we store on them. However, as effective as these measures
+                     are, no security system is impenetrable. We cannot completely
+                     guarantee the security of our database, nor can we guarantee that
+                     the information you supply will not be intercepted while being
+                     transmitted to us over the Internet.
+                </p>
+            </section>
+            <section id="changes">
+                <span className="nav-spacer" />
+                <h3>Notifications of Changes to the Privacy Policy</h3>
+                <p>
+                    We review our security measures and Privacy Policy on a periodic
+                     basis, and we may modify our policies as appropriate. We may also
+                     change or update our Privacy Policy if we add new services or
+                     features. If we make any changes to our privacy practices, we will
+                     amend this Privacy Policy accordingly and post the amended policy
+                     on the Scratch website. We encourage you to review our Privacy
+                     Policy on a regular basis.
+                </p>
+            </section>
+            <p>The Scratch Privacy Policy was last updated: November 2016</p>
+        </div>
+        <nav>
+            <ol>
+                <li><a href="#collection">What information does the Scratch Team collect about me?</a></li>
+                <li><a href="#usage">How does the Scratch Team use my information?</a></li>
+                <li><a href="#update">How can I update my personal information?</a></li>
+                <li><a href="#protection">How does the Scratch Team protect my information?</a></li>
+                <li><a href="#changes">Notifications of Changes to the Privacy Policy</a></li>
+            </ol>
+        </nav>
+    </InformationPage>
+);
 
 render(<Page><Privacypolicy /></Page>, document.getElementById('app'));
diff --git a/src/views/search/search.jsx b/src/views/search/search.jsx
index be30ee8ab..dbdf6e93f 100644
--- a/src/views/search/search.jsx
+++ b/src/views/search/search.jsx
@@ -1,45 +1,39 @@
-var injectIntl = require('react-intl').injectIntl;
-var FormattedMessage = require('react-intl').FormattedMessage;
-var React = require('react');
-var connect = require('react-redux').connect;
-var render = require('../../lib/render.jsx');
+const bindAll = require('lodash.bindall');
+const connect = require('react-redux').connect;
+const FormattedMessage = require('react-intl').FormattedMessage;
+const injectIntl = require('react-intl').injectIntl;
+const intlShape = require('react-intl').intlShape;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var api = require('../../lib/api');
+const api = require('../../lib/api');
+const Button = require('../../components/forms/button.jsx');
+const Grid = require('../../components/grid/grid.jsx');
+const navigationActions = require('../../redux/navigation.js');
+const TitleBanner = require('../../components/title-banner/title-banner.jsx');
+const Tabs = require('../../components/tabs/tabs.jsx');
 
-var Page = require('../../components/page/www/page.jsx');
-var TitleBanner = require('../../components/title-banner/title-banner.jsx');
-var Button = require('../../components/forms/button.jsx');
-var Tabs = require('../../components/tabs/tabs.jsx');
-var Grid = require('../../components/grid/grid.jsx');
-var navigationActions = require('../../redux/navigation.js');
+const Page = require('../../components/page/www/page.jsx');
+const render = require('../../lib/render.jsx');
 
 require('./search.scss');
 
-// @todo migrate to React-Router once available
-var Search = injectIntl(React.createClass({
-    type: 'Search',
-    getDefaultProps: function () {
-        var pathname = window.location.pathname.toLowerCase();
-        if (pathname[pathname.length - 1] === '/') {
-            pathname = pathname.substring(0, pathname.length - 1);
-        }
-        var start = pathname.lastIndexOf('/');
-        var type = pathname.substring(start + 1, pathname.length);
-        return {
-            tab: type,
-            loadNumber: 16
-        };
-    },
-    getInitialState: function () {
-        return {
-            loaded: [],
-            offset: 0
-        };
-    },
-    componentDidMount: function () {
-        var query = window.location.search;
-        var q = query.lastIndexOf('q=');
-        var term = '';
+class Search extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'getSearchState',
+            'handleGetSearchMore',
+            'getTab'
+        ]);
+        this.state = this.getSearchState();
+        this.state.loaded = [];
+        this.state.loadNumber = 16;
+    }
+    componentDidMount () {
+        const query = window.location.search;
+        const q = query.lastIndexOf('q=');
+        let term = '';
         if (q !== -1) {
             term = query.substring(q + 2, query.length).toLowerCase();
         }
@@ -51,89 +45,130 @@ var Search = injectIntl(React.createClass({
         }
         term = decodeURI(term.split('+').join(' '));
         this.props.dispatch(navigationActions.setSearchTerm(term));
-    },
-    componentDidUpdate: function (prevProps) {
-        if (this.props.searchTerm !== prevProps.searchTerm) this.getSearchMore();
-    },
-    getSearchMore: function () {
-        var termText = '';
-        if (this.props.searchTerm !== '') {
-            termText = '&q=' + encodeURIComponent(this.props.searchTerm.split(' ').join('+'));
+    }
+    componentDidUpdate (prevProps) {
+        if (this.props.searchTerm !== prevProps.searchTerm) {
+            this.getSearchMore();
         }
+    }
+    getSearchState () {
+        let pathname = window.location.pathname.toLowerCase();
+        if (pathname[pathname.length - 1] === '/') {
+            pathname = pathname.substring(0, pathname.length - 1);
+        }
+        const start = pathname.lastIndexOf('/');
+        const type = pathname.substring(start + 1, pathname.length);
+        return {
+            tab: type,
+            loadNumber: 16
+        };
+    }
+    handleGetSearchMore () {
+        let termText = '';
+        if (this.props.searchTerm !== '') {
+            termText = `&q=${encodeURIComponent(this.props.searchTerm.split(' ').join('+'))}`;
+        }
+        const locale = this.props.intl.locale;
+        const loadNumber = this.props.loadNumber;
+        const offset = this.state.offset;
+        const queryString = `limit=${loadNumber}&offset=${offset}&language=${locale}&mode=popular${termText}`;
+
         api({
-            uri: '/search/' + this.props.tab +
-                 '?limit=' + this.props.loadNumber +
-                 '&offset=' + this.state.offset +
-                 '&language=' + this.props.intl.locale +
-                 '&mode=popular' +
-                 termText
-        }, function (err, body) {
-            var loadedSoFar = this.state.loaded;
+            uri: `/search/${this.props.tab}?${queryString}`
+        }, (err, body) => {
+            const loadedSoFar = this.state.loaded;
             Array.prototype.push.apply(loadedSoFar, body);
-            this.setState({loaded: loadedSoFar});
-            var currentOffset = this.state.offset + this.props.loadNumber;
-            this.setState({offset: currentOffset});
-        }.bind(this));
-    },
-    onSearchSubmit: function (formData) {
-        window.location.href = '/search/projects?q=' + encodeURIComponent(formData.q);
-    },
-    getTab: function (type) {
-        var term = this.props.searchTerm.split(' ').join('+');
-        var allTab = <a href={'/search/' + type + '?q=' + term + '/'}>
-                        <li>
-                            <img src={'/svgs/tabs/' + type + '-inactive.svg'} className={'tab-icon ' + type} />
-                            <FormattedMessage id={'general.' + type} />
-                        </li>
-                    </a>;
-        if (this.props.tab == type) {
-            allTab = <a href={'/search/' + type + '?q=' + term + '/'}>
-                        <li className='active'>
-                            <img src={'/svgs/tabs/' + type + '-active.svg'} className={'tab-icon ' + type} />
-                            <FormattedMessage id={'general.' + type} />
-                        </li>
-                    </a>;
+            const currentOffset = this.state.offset + this.props.loadNumber;
+
+            this.setState({
+                loaded: loadedSoFar,
+                offset: currentOffset
+            });
+        });
+    }
+    getTab (type) {
+        const term = this.props.searchTerm.split(' ').join('+');
+        let allTab = (
+            <a href={`/search/${type}?q=${term}/`}>
+                <li>
+                    <img
+                        className={`tab-icon ${type}`}
+                        src={`/svgs/tabs/${type}-inactive.svg`}
+                    />
+                    <FormattedMessage id={`general.${type}`} />
+                </li>
+            </a>
+        );
+        if (this.props.tab === type) {
+            allTab = (
+                <a href={`/search/${type}?q=${term}/`}>
+                    <li className="active">
+                        <img
+                            className={`tab-icon ${type}`}
+                            src={`/svgs/tabs/${type}-active.svg`}
+                        />
+                        <FormattedMessage id={`general.${type}`} />
+                    </li>
+                </a>
+            );
         }
         return allTab;
-    },
-    render: function () {
+    }
+    render () {
         return (
             <div>
-                <div className='outer'>
-                        <TitleBanner className="masthead">
-                            <div className="inner">
-                                <h1 className="title-banner-h1"><FormattedMessage id="general.search" /></h1>
-                            </div>
-                        </TitleBanner>
-                        <Tabs>
-                            {this.getTab('projects')}
-                            {this.getTab('studios')}
-                        </Tabs>
-                        <div id='projectBox' key='projectBox'>
-                        <Grid items={this.state.loaded}
-                              itemType={this.props.tab}
-                              cards={true}
-                              showAvatar={true}
-                              showLoves={false}
-                              showFavorites={false}
-                              showViews={false} />
-                         <Button onClick={this.getSearchMore} className="white">
-                            <FormattedMessage id='general.loadMore' />
-                        </Button>
+                <div className="outer">
+                    <TitleBanner className="masthead">
+                        <div className="inner">
+                            <h1 className="title-banner-h1">
+                                <FormattedMessage id="general.search" />
+                            </h1>
                         </div>
+                    </TitleBanner>
+                    <Tabs>
+                        {this.getTab('projects')}
+                        {this.getTab('studios')}
+                    </Tabs>
+                    <div
+                        id="projectBox"
+                        key="projectBox"
+                    >
+                        <Grid
+                            cards
+                            showAvatar
+                            itemType={this.props.tab}
+                            items={this.state.loaded}
+                            showFavorites={false}
+                            showLoves={false}
+                            showViews={false}
+                        />
+                        <Button
+                            className="white"
+                            onClick={this.handleGetSearchMore}
+                        >
+                            <FormattedMessage id="general.loadMore" />
+                        </Button>
+                    </div>
                 </div>
             </div>
         );
     }
-}));
+}
 
-var mapStateToProps = function (state) {
-    return {
-        searchTerm: state.navigation
-    };
+Search.propTypes = {
+    dispatch: PropTypes.func,
+    intl: intlShape,
+    loadNumber: PropTypes.number,
+    searchTerm: PropTypes.string,
+    tab: PropTypes.string
 };
 
-var ConnectedSearch = connect(mapStateToProps)(Search);
+const mapStateToProps = state => ({
+    searchTerm: state.navigation
+});
+
+const WrappedSearch = injectIntl(Search);
+const ConnectedSearch = connect(mapStateToProps)(WrappedSearch);
 
 render(
     <Page><ConnectedSearch /></Page>,
diff --git a/src/views/splash/activity-rows/become-curator.jsx b/src/views/splash/activity-rows/become-curator.jsx
index 9ca360739..c220c92ff 100644
--- a/src/views/splash/activity-rows/become-curator.jsx
+++ b/src/views/splash/activity-rows/become-curator.jsx
@@ -1,49 +1,43 @@
-var classNames = require('classnames');
-var FormattedMessage = require('react-intl').FormattedMessage;
-var React = require('react');
+const classNames = require('classnames');
+const FormattedMessage = require('react-intl').FormattedMessage;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var SocialMessage = require('../../../components/social-message/social-message.jsx');
+const SocialMessage = require('../../../components/social-message/social-message.jsx');
 
-var BecomeCuratorMessage = React.createClass({
-    type: 'BecomeCuratorMessage',
-    propTypes: {
-        actorUsername: React.PropTypes.string.isRequired,
-        studioId: React.PropTypes.number.isRequired,
-        studioTitle: React.PropTypes.string.isRequired,
-        datetimePromoted: React.PropTypes.string.isRequired
-    },
-    render: function () {
-        var actorUri = '/users/' + this.props.actorUsername + '/';
-        var studioUri = '/studios/' + this.props.studioId + '/';
-
-        var classes = classNames(
+const BecomeCuratorMessage = props => (
+    <SocialMessage
+        as="div"
+        className={classNames(
             'mod-become-curator',
-            this.props.className
-        );
-        return (
-            <SocialMessage
-                as="div"
-                className={classes}
-                datetime={this.props.datetimePromoted}
-            >
-                <FormattedMessage
-                    id='messages.becomeCuratorText'
-                    values={{
-                        username: (
-                            <a
-                                href={actorUri}
-                            >
-                                {this.props.actorUsername}
-                            </a>
-                        ),
-                        studio: (
-                            <a href={studioUri}>{this.props.studioTitle}</a>
-                        )
-                    }}
-                />
-            </SocialMessage>
-        );
-    }
-});
+            props.className
+        )}
+        datetime={props.datetimePromoted}
+    >
+        <FormattedMessage
+            id="messages.becomeCuratorText"
+            values={{
+                username: (
+                    <a href={`/users/${props.actorUsername}/`}>
+                        {props.actorUsername}
+                    </a>
+                ),
+                studio: (
+                    <a href={`/studios/${props.studioId}/`}>
+                        {props.studioTitle}
+                    </a>
+                )
+            }}
+        />
+    </SocialMessage>
+);
+
+BecomeCuratorMessage.propTypes = {
+    actorUsername: PropTypes.string.isRequired,
+    className: PropTypes.string,
+    datetimePromoted: PropTypes.string.isRequired,
+    studioId: PropTypes.number.isRequired,
+    studioTitle: PropTypes.string.isRequired
+};
 
 module.exports = BecomeCuratorMessage;
diff --git a/src/views/splash/activity-rows/become-manager.jsx b/src/views/splash/activity-rows/become-manager.jsx
index 7e75adb38..5dee06fe9 100644
--- a/src/views/splash/activity-rows/become-manager.jsx
+++ b/src/views/splash/activity-rows/become-manager.jsx
@@ -1,49 +1,43 @@
-var classNames = require('classnames');
-var FormattedMessage = require('react-intl').FormattedMessage;
-var React = require('react');
+const classNames = require('classnames');
+const FormattedMessage = require('react-intl').FormattedMessage;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var SocialMessage = require('../../../components/social-message/social-message.jsx');
+const SocialMessage = require('../../../components/social-message/social-message.jsx');
 
-var BecomeManagerMessage = React.createClass({
-    type: 'BecomeManagerMessage',
-    propTypes: {
-        recipientUsername: React.PropTypes.string.isRequired,
-        studioId: React.PropTypes.number.isRequired,
-        studioTitle: React.PropTypes.string.isRequired,
-        datetimePromoted: React.PropTypes.string.isRequired
-    },
-    render: function () {
-        var recipientUri = '/users/' + this.props.recipientUsername + '/';
-        var studioUri = '/studios/' + this.props.studioId + '/';
-
-        var classes = classNames(
+const BecomeManagerMessage = props => (
+    <SocialMessage
+        as="div"
+        className={classNames(
             'mod-become-manager',
-            this.props.className
-        );
-        return (
-            <SocialMessage
-                as="div"
-                className={classes}
-                datetime={this.props.datetimePromoted}
-            >
-                <FormattedMessage
-                    id='messages.becomeManagerText'
-                    values={{
-                        username: (
-                            <a
-                                href={recipientUri}
-                            >
-                                {this.props.recipientUsername}
-                            </a>
-                        ),
-                        studio: (
-                            <a href={studioUri}>{this.props.studioTitle}</a>
-                        )
-                    }}
-                />
-            </SocialMessage>
-        );
-    }
-});
+            props.className
+        )}
+        datetime={props.datetimePromoted}
+    >
+        <FormattedMessage
+            id="messages.becomeManagerText"
+            values={{
+                username: (
+                    <a href={`/users/${props.recipientUsername}/`}>
+                        {props.recipientUsername}
+                    </a>
+                ),
+                studio: (
+                    <a href={`/studios/${props.studioId}/`}>
+                        {props.studioTitle}
+                    </a>
+                )
+            }}
+        />
+    </SocialMessage>
+);
+
+BecomeManagerMessage.propTypes = {
+    className: PropTypes.string,
+    datetimePromoted: PropTypes.string.isRequired,
+    recipientUsername: PropTypes.string.isRequired,
+    studioId: PropTypes.number.isRequired,
+    studioTitle: PropTypes.string.isRequired
+};
 
 module.exports = BecomeManagerMessage;
diff --git a/src/views/splash/activity-rows/favorite-project.jsx b/src/views/splash/activity-rows/favorite-project.jsx
index 41b14c55c..8775fd024 100644
--- a/src/views/splash/activity-rows/favorite-project.jsx
+++ b/src/views/splash/activity-rows/favorite-project.jsx
@@ -1,49 +1,43 @@
-var classNames = require('classnames');
-var FormattedMessage = require('react-intl').FormattedMessage;
-var React = require('react');
+const classNames = require('classnames');
+const FormattedMessage = require('react-intl').FormattedMessage;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var SocialMessage = require('../../../components/social-message/social-message.jsx');
+const SocialMessage = require('../../../components/social-message/social-message.jsx');
 
-var FavoriteProjectMessage = React.createClass({
-    type: 'FavoriteProjectMessage',
-    propTypes: {
-        actorUsername: React.PropTypes.string.isRequired,
-        projectId: React.PropTypes.number.isRequired,
-        projectTitle: React.PropTypes.string.isRequired,
-        favoriteDateTime: React.PropTypes.string.isRequired
-    },
-    render: function () {
-        var projectLink = '/projects/' + this.props.projectId;
-        var profileLink = '/users/' + this.props.actorUsername;
-
-        var classes = classNames(
+const FavoriteProjectMessage = props => (
+    <SocialMessage
+        as="div"
+        className={classNames(
             'mod-love-favorite',
-            this.props.className
-        );
-        return (
-            <SocialMessage
-                as="div"
-                className={classes}
-                datetime={this.props.favoriteDateTime}
-            >
-                <FormattedMessage
-                    id='messages.favoriteText'
-                    values={{
-                        profileLink: (
-                            <a
-                                href={profileLink}
-                            >
-                                {this.props.actorUsername}
-                            </a>
-                        ),
-                        projectLink: (
-                            <a href={projectLink}>{this.props.projectTitle}</a>
-                        )
-                    }}
-                />
-            </SocialMessage>
-        );
-    }
-});
+            props.className
+        )}
+        datetime={props.favoriteDateTime}
+    >
+        <FormattedMessage
+            id="messages.favoriteText"
+            values={{
+                profileLink: (
+                    <a href={`/users/${props.actorUsername}`}>
+                        {props.actorUsername}
+                    </a>
+                ),
+                projectLink: (
+                    <a href={`/projects/${props.projectId}`}>
+                        {props.projectTitle}
+                    </a>
+                )
+            }}
+        />
+    </SocialMessage>
+);
+
+FavoriteProjectMessage.propTypes = {
+    actorUsername: PropTypes.string.isRequired,
+    className: PropTypes.string,
+    favoriteDateTime: PropTypes.string.isRequired,
+    projectId: PropTypes.number.isRequired,
+    projectTitle: PropTypes.string.isRequired
+};
 
 module.exports = FavoriteProjectMessage;
diff --git a/src/views/splash/activity-rows/follow.jsx b/src/views/splash/activity-rows/follow.jsx
index addf202f1..bb1f13baa 100644
--- a/src/views/splash/activity-rows/follow.jsx
+++ b/src/views/splash/activity-rows/follow.jsx
@@ -1,62 +1,55 @@
-var classNames = require('classnames');
-var FormattedMessage = require('react-intl').FormattedMessage;
-var React = require('react');
+const classNames = require('classnames');
+const FormattedMessage = require('react-intl').FormattedMessage;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var SocialMessage = require('../../../components/social-message/social-message.jsx');
+const SocialMessage = require('../../../components/social-message/social-message.jsx');
 
-var FollowMessage = React.createClass({
-    type: 'FollowMessage',
-    propTypes: {
-        followerUsername: React.PropTypes.string.isRequired,
-        followeeId: React.PropTypes.string.isRequired,
-        followeeTitle: React.PropTypes.string,
-        followDateTime: React.PropTypes.string.isRequired
-    },
-    render: function () {
-        var profileLink = '/users/' + this.props.followerUsername; + '/';
-        
-        var followeeLink = '';
-        var followeeTitle = '';
-        if (typeof this.props.followeeTitle !== 'undefined') {
-            followeeLink = '/studios/' + this.props.followeeId;
-            followeeTitle = this.props.followeeTitle;
-        } else {
-            followeeLink = '/users/' + this.props.followeeId;
-            followeeTitle = this.props.followeeId;
-        }
-        
-        var classes = classNames(
-            'mod-follow-user',
-            this.props.className
-        );
-        return (
-            <SocialMessage
-                as="div"
-                className={classes}
-                datetime={this.props.followDateTime}
-            >
-                <FormattedMessage
-                    id='messages.followText'
-                    values={{
-                        profileLink: (
-                            <a
-                                href={profileLink}
-                            >
-                                {this.props.followerUsername}
-                            </a>
-                        ),
-                        followeeLink: (
-                            <a
-                                href={followeeLink}
-                            >
-                                {followeeTitle}
-                            </a>
-                        )
-                    }}
-                />
-            </SocialMessage>
-        );
+const FollowMessage = props => {
+    let followeeLink = '';
+    let followeeTitle = '';
+    if (typeof props.followeeTitle === 'undefined') {
+        followeeLink = `/users/${props.followeeId}`;
+        followeeTitle = props.followeeId;
+    } else {
+        followeeLink = `/studios/${props.followeeId}`;
+        followeeTitle = props.followeeTitle;
     }
-});
+        
+    return (
+        <SocialMessage
+            as="div"
+            className={classNames(
+                'mod-follow-user',
+                props.className
+            )}
+            datetime={props.followDateTime}
+        >
+            <FormattedMessage
+                id="messages.followText"
+                values={{
+                    profileLink: (
+                        <a href={`/users/${props.followerUsername}/`}>
+                            {props.followerUsername}
+                        </a>
+                    ),
+                    followeeLink: (
+                        <a href={followeeLink}>
+                            {followeeTitle}
+                        </a>
+                    )
+                }}
+            />
+        </SocialMessage>
+    );
+};
+
+FollowMessage.propTypes = {
+    className: PropTypes.string,
+    followDateTime: PropTypes.string.isRequired,
+    followeeId: PropTypes.string.isRequired,
+    followeeTitle: PropTypes.string,
+    followerUsername: PropTypes.string.isRequired
+};
 
 module.exports = FollowMessage;
diff --git a/src/views/splash/activity-rows/love-project.jsx b/src/views/splash/activity-rows/love-project.jsx
index 5bab6d38d..0783c0cb3 100644
--- a/src/views/splash/activity-rows/love-project.jsx
+++ b/src/views/splash/activity-rows/love-project.jsx
@@ -1,49 +1,43 @@
-var classNames = require('classnames');
-var FormattedMessage = require('react-intl').FormattedMessage;
-var React = require('react');
+const classNames = require('classnames');
+const FormattedMessage = require('react-intl').FormattedMessage;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var SocialMessage = require('../../../components/social-message/social-message.jsx');
+const SocialMessage = require('../../../components/social-message/social-message.jsx');
 
-var LoveProjectMessage = React.createClass({
-    type: 'LoveProjectMessage',
-    propTypes: {
-        actorUsername: React.PropTypes.string.isRequired,
-        projectId: React.PropTypes.number.isRequired,
-        projectTitle: React.PropTypes.string.isRequired,
-        loveDateTime: React.PropTypes.string.isRequired
-    },
-    render: function () {
-        var projectLink = '/projects/' + this.props.projectId;
-        var profileLink = '/users/' + this.props.actorUsername;
-
-        var classes = classNames(
+const LoveProjectMessage = props => (
+    <SocialMessage
+        as="div"
+        className={classNames(
             'mod-love-project',
-            this.props.className
-        );
-        return (
-            <SocialMessage
-                as="div"
-                className={classes}
-                datetime={this.props.loveDateTime}
-            >
-                <FormattedMessage
-                    id='messages.loveText'
-                    values={{
-                        profileLink: (
-                            <a
-                                href={profileLink}
-                            >
-                                {this.props.actorUsername}
-                            </a>
-                        ),
-                        projectLink: (
-                            <a href={projectLink}>{this.props.projectTitle}</a>
-                        )
-                    }}
-                />
-            </SocialMessage>
-        );
-    }
-});
+            props.className
+        )}
+        datetime={props.loveDateTime}
+    >
+        <FormattedMessage
+            id="messages.loveText"
+            values={{
+                profileLink: (
+                    <a href={`/users/${props.actorUsername}`}>
+                        {props.actorUsername}
+                    </a>
+                ),
+                projectLink: (
+                    <a href={`/projects/${props.projectId}`}>
+                        {props.projectTitle}
+                    </a>
+                )
+            }}
+        />
+    </SocialMessage>
+);
+
+LoveProjectMessage.propTypes = {
+    actorUsername: PropTypes.string.isRequired,
+    className: PropTypes.string,
+    loveDateTime: PropTypes.string.isRequired,
+    projectId: PropTypes.number.isRequired,
+    projectTitle: PropTypes.string.isRequired
+};
 
 module.exports = LoveProjectMessage;
diff --git a/src/views/splash/activity-rows/remix-project.jsx b/src/views/splash/activity-rows/remix-project.jsx
index b00f5e2da..67b8b1f4b 100644
--- a/src/views/splash/activity-rows/remix-project.jsx
+++ b/src/views/splash/activity-rows/remix-project.jsx
@@ -1,55 +1,50 @@
-var classNames = require('classnames');
-var FormattedMessage = require('react-intl').FormattedMessage;
-var React = require('react');
+const classNames = require('classnames');
+const FormattedMessage = require('react-intl').FormattedMessage;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var SocialMessage = require('../../../components/social-message/social-message.jsx');
+const SocialMessage = require('../../../components/social-message/social-message.jsx');
 
-var RemixProjectMessage = React.createClass({
-    type: 'RemixProjectMessage',
-    propTypes: {
-        actorUsername: React.PropTypes.string.isRequired,
-        projectId: React.PropTypes.number.isRequired,
-        projectTitle: React.PropTypes.string.isRequired,
-        parentId: React.PropTypes.number.isRequired,
-        parentTitle: React.PropTypes.string.isRequired,
-        remixDate: React.PropTypes.string.isRequired
-    },
-    render: function () {
-        var projectLink = '/projects/' + this.props.projectId;
-        var profileLink = '/users/' + this.props.actorUsername;
-        var remixedProjectLink = '/projects/' + this.props.parentId;
-        
-        var classes = classNames(
+const RemixProjectMessage = props => (
+    <SocialMessage
+        as="div"
+        className={classNames(
             'mod-remix-project',
-            this.props.className
-        );
-        return (
-            <SocialMessage
-                as="div"
-                className={classes}
-                datetime={this.props.remixDate}
-            >
-                <FormattedMessage
-                    id='messages.remixText'
-                    values={{
-                        profileLink: (
-                            <a
-                                href={profileLink}
-                            >
-                                {this.props.actorUsername}
-                            </a>
-                        ),
-                        projectLink: (
-                            <a href={projectLink}>{this.props.projectTitle}</a>
-                        ),
-                        remixedProjectLink: (
-                            <a href={remixedProjectLink}>{this.props.parentTitle}</a>
-                        )
-                    }}
-                />
-            </SocialMessage>
-        );
-    }
-});
+            props.className
+        )}
+        datetime={props.remixDate}
+    >
+        <FormattedMessage
+            id="messages.remixText"
+            values={{
+                profileLink: (
+                    <a href={`/users/${props.actorUsername}`}>
+                        {props.actorUsername}
+                    </a>
+                ),
+                projectLink: (
+                    <a href={`/projects/${props.projectId}`}>
+                        {props.projectTitle}
+                    </a>
+                ),
+                remixedProjectLink: (
+                    <a href={`/projects/${props.parentId}`}>
+                        {props.parentTitle}
+                    </a>
+                )
+            }}
+        />
+    </SocialMessage>
+);
+
+RemixProjectMessage.propTypes = {
+    actorUsername: PropTypes.string.isRequired,
+    className: PropTypes.string,
+    parentId: PropTypes.number.isRequired,
+    parentTitle: PropTypes.string.isRequired,
+    projectId: PropTypes.number.isRequired,
+    projectTitle: PropTypes.string.isRequired,
+    remixDate: PropTypes.string.isRequired
+};
 
 module.exports = RemixProjectMessage;
diff --git a/src/views/splash/activity-rows/share-project.jsx b/src/views/splash/activity-rows/share-project.jsx
index a1a820f6e..71ed56ef1 100644
--- a/src/views/splash/activity-rows/share-project.jsx
+++ b/src/views/splash/activity-rows/share-project.jsx
@@ -1,49 +1,43 @@
-var classNames = require('classnames');
-var FormattedMessage = require('react-intl').FormattedMessage;
-var React = require('react');
+const classNames = require('classnames');
+const FormattedMessage = require('react-intl').FormattedMessage;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var SocialMessage = require('../../../components/social-message/social-message.jsx');
+const SocialMessage = require('../../../components/social-message/social-message.jsx');
 
-var ShareProjectMessage = React.createClass({
-    type: 'ShareProjectMessage',
-    propTypes: {
-        actorUsername: React.PropTypes.string.isRequired,
-        projectId: React.PropTypes.number.isRequired,
-        projectTitle: React.PropTypes.string.isRequired,
-        loveDateTime: React.PropTypes.string.isRequired
-    },
-    render: function () {
-        var projectLink = '/projects/' + this.props.projectId;
-        var profileLink = '/users/' + this.props.actorUsername;
-
-        var classes = classNames(
+const ShareProjectMessage = props => (
+    <SocialMessage
+        as="div"
+        className={classNames(
             'mod-love-project',
-            this.props.className
-        );
-        return (
-            <SocialMessage
-                as="div"
-                className={classes}
-                datetime={this.props.loveDateTime}
-            >
-                <FormattedMessage
-                    id='messages.shareText'
-                    values={{
-                        profileLink: (
-                            <a
-                                href={profileLink}
-                            >
-                                {this.props.actorUsername}
-                            </a>
-                        ),
-                        projectLink: (
-                            <a href={projectLink}>{this.props.projectTitle}</a>
-                        )
-                    }}
-                />
-            </SocialMessage>
-        );
-    }
-});
+            props.className
+        )}
+        datetime={props.loveDateTime}
+    >
+        <FormattedMessage
+            id="messages.shareText"
+            values={{
+                profileLink: (
+                    <a href={`/users/${props.actorUsername}`}>
+                        {props.actorUsername}
+                    </a>
+                ),
+                projectLink: (
+                    <a href={`/projects/${props.projectId}`}>
+                        {props.projectTitle}
+                    </a>
+                )
+            }}
+        />
+    </SocialMessage>
+);
+
+ShareProjectMessage.propTypes = {
+    actorUsername: PropTypes.string.isRequired,
+    className: PropTypes.string,
+    loveDateTime: PropTypes.string.isRequired,
+    projectId: PropTypes.number.isRequired,
+    projectTitle: PropTypes.string.isRequired
+};
 
 module.exports = ShareProjectMessage;
diff --git a/src/views/splash/presentation.jsx b/src/views/splash/presentation.jsx
index 91baf5d7c..a76a7aa94 100644
--- a/src/views/splash/presentation.jsx
+++ b/src/views/splash/presentation.jsx
@@ -1,135 +1,158 @@
-var FormattedMessage = require('react-intl').FormattedMessage;
-var injectIntl = require('react-intl').injectIntl;
-var React = require('react');
+const bindAll = require('lodash.bindall');
+const FormattedMessage = require('react-intl').FormattedMessage;
+const injectIntl = require('react-intl').injectIntl;
+const intlShape = require('react-intl').intlShape;
+const MediaQuery = require('react-responsive').default;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var sessionActions = require('../../redux/session.js');
-var shuffle = require('../../lib/shuffle.js').shuffle;
+const frameless = require('../../lib/frameless');
+const sessionActions = require('../../redux/session.js');
+const shuffle = require('../../lib/shuffle.js').shuffle;
 
-var AdminPanel = require('../../components/adminpanel/adminpanel.jsx');
-var DropdownBanner = require('../../components/dropdown-banner/banner.jsx');
-var Box = require('../../components/box/box.jsx');
-var Button = require('../../components/forms/button.jsx');
-var Carousel = require('../../components/carousel/carousel.jsx');
-var LegacyCarousel = require('../../components/carousel/legacy-carousel.jsx');
-var Intro = require('../../components/intro/intro.jsx');
-var IframeModal = require('../../components/modal/iframe/modal.jsx');
-var News = require('../../components/news/news.jsx');
-var TeacherBanner = require('../../components/teacher-banner/teacher-banner.jsx');
-var Welcome = require('../../components/welcome/welcome.jsx');
+const AdminPanel = require('../../components/adminpanel/adminpanel.jsx');
+const Box = require('../../components/box/box.jsx');
+const Button = require('../../components/forms/button.jsx');
+const Carousel = require('../../components/carousel/carousel.jsx');
+const DropdownBanner = require('../../components/dropdown-banner/banner.jsx');
+const IframeModal = require('../../components/modal/iframe/modal.jsx');
+const Intro = require('../../components/intro/intro.jsx');
+const LegacyCarousel = require('../../components/carousel/legacy-carousel.jsx');
+const News = require('../../components/news/news.jsx');
+const TeacherBanner = require('../../components/teacher-banner/teacher-banner.jsx');
+const Welcome = require('../../components/welcome/welcome.jsx');
 
 // Activity Components
-var BecomeCuratorMessage = require('./activity-rows/become-curator.jsx');
-var BecomeManagerMessage = require('./activity-rows/become-manager.jsx');
-var FavoriteProjectMessage = require('./activity-rows/favorite-project.jsx');
-var FollowMessage = require('./activity-rows/follow.jsx');
-var LoveProjectMessage = require('./activity-rows/love-project.jsx');
-var RemixProjectMessage = require('./activity-rows/remix-project.jsx');
-var ShareProjectMessage = require('./activity-rows/share-project.jsx');
-
-var MediaQuery = require('react-responsive');
-var frameless = require('../../lib/frameless');
+const BecomeCuratorMessage = require('./activity-rows/become-curator.jsx');
+const BecomeManagerMessage = require('./activity-rows/become-manager.jsx');
+const FavoriteProjectMessage = require('./activity-rows/favorite-project.jsx');
+const FollowMessage = require('./activity-rows/follow.jsx');
+const LoveProjectMessage = require('./activity-rows/love-project.jsx');
+const RemixProjectMessage = require('./activity-rows/remix-project.jsx');
+const ShareProjectMessage = require('./activity-rows/share-project.jsx');
 
 require('./splash.scss');
 
-var ActivityList = injectIntl(React.createClass({
-    propTypes: {
-        items: React.PropTypes.array
-    },
-    getComponentForMessage: function (message) {
-        var key = message.type + '_' + message.id;
+class ActivityList extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'getComponentForMessage'
+        ]);
+    }
+    getComponentForMessage (message) {
+        const key = `${message.type}_${message.datetime_created}`;
 
         switch (message.type) {
         case 'followuser':
-            return <FollowMessage
-                key={key}
-                followerUsername={message.actor_username}
-                followeeId={message.followed_username}
-                followDateTime={message.datetime_created}
-            />;
+            return (
+                <FollowMessage
+                    followDateTime={message.datetime_created}
+                    followeeId={message.followed_username}
+                    followerUsername={message.actor_username}
+                    key={key}
+                />
+            );
         case 'followstudio':
-            return <FollowMessage
-                key={key}
-                followerUsername={message.actor_username}
-                followeeId={message.gallery_id}
-                followeeTitle={message.title}
-                followDateTime={message.datetime_created}
-            />;
+            return (
+                <FollowMessage
+                    followDateTime={message.datetime_created}
+                    followeeId={message.gallery_id}
+                    followeeTitle={message.title}
+                    followerUsername={message.actor_username}
+                    key={key}
+                />
+            );
         case 'loveproject':
-            return <LoveProjectMessage
-                key={key}
-                actorUsername={message.actor_username}
-                projectId={message.project_id}
-                projectTitle={message.title}
-                loveDateTime={message.datetime_created}
-            />;
+            return (
+                <LoveProjectMessage
+                    actorUsername={message.actor_username}
+                    key={key}
+                    loveDateTime={message.datetime_created}
+                    projectId={message.project_id}
+                    projectTitle={message.title}
+                />
+            );
         case 'favoriteproject':
-            return <FavoriteProjectMessage
-                key={key}
-                actorUsername={message.actor_username}
-                projectId={message.project_id}
-                projectTitle={message.project_title}
-                favoriteDateTime={message.datetime_created}
-            />;
+            return (
+                <FavoriteProjectMessage
+                    actorUsername={message.actor_username}
+                    favoriteDateTime={message.datetime_created}
+                    key={key}
+                    projectId={message.project_id}
+                    projectTitle={message.project_title}
+                />
+            );
         case 'remixproject':
-            return <RemixProjectMessage
-                key={key}
-                actorUsername={message.actor_username}
-                projectId={message.project_id}
-                projectTitle={message.title}
-                parentId={message.parent_id}
-                parentTitle={message.parent_title}
-                remixDate={message.datetime_created}
-            />;
+            return (
+                <RemixProjectMessage
+                    actorUsername={message.actor_username}
+                    key={key}
+                    parentId={message.parent_id}
+                    parentTitle={message.parent_title}
+                    projectId={message.project_id}
+                    projectTitle={message.title}
+                    remixDate={message.datetime_created}
+                />
+            );
         case 'becomecurator':
-            return <BecomeCuratorMessage
-                key={key}
-                actorUsername={message.actor_username}
-                studioId={message.gallery_id}
-                studioTitle={message.title}
-                datetimePromoted={message.datetime_created}
-            />;
+            return (
+                <BecomeCuratorMessage
+                    actorUsername={message.actor_username}
+                    datetimePromoted={message.datetime_created}
+                    key={key}
+                    studioId={message.gallery_id}
+                    studioTitle={message.title}
+                />
+            );
         case 'becomeownerstudio':
-            return <BecomeManagerMessage
-                key={key}
-                recipientUsername={message.recipient_username}
-                studioId={message.gallery_id}
-                studioTitle={message.gallery_title}
-                datetimePromoted={message.datetime_created}
-            />;
+            return (
+                <BecomeManagerMessage
+                    datetimePromoted={message.datetime_created}
+                    key={key}
+                    recipientUsername={message.recipient_username}
+                    studioId={message.gallery_id}
+                    studioTitle={message.gallery_title}
+                />
+            );
         case 'shareproject':
-            return <ShareProjectMessage
-                key={key}
-                actorUsername={message.actor_username}
-                projectId={message.project_id}
-                projectTitle={message.title}
-                loveDateTime={message.datetime_created}
-            />;
+            return (
+                <ShareProjectMessage
+                    actorUsername={message.actor_username}
+                    key={key}
+                    loveDateTime={message.datetime_created}
+                    projectId={message.project_id}
+                    projectTitle={message.title}
+                />
+            );
         }
-    },
-    render: function () {
-        var formatMessage = this.props.intl.formatMessage;
+    }
+    render () {
         return (
             <Box
                 className="activity"
-                title={formatMessage({id: 'general.whatsHappening'})}
+                title={this.props.intl.formatMessage({
+                    id: 'general.whatsHappening'
+                })}
             >
                 {this.props.items && this.props.items.length > 0 ? [
                     <ul
                         className="activity-ul"
                         key="activity-ul"
                     >
-                        {this.props.items.map(function (item) {
-                            var profileLink = '/users/' + item.actor_username; + '/';
-                            var profileThumbUrl = '//uploads.scratch.mit.edu/users/avatars/' + item.actor_id + '.png';
+                        {this.props.items.map(item => {
+                            let profileLink = `/users/${item.actor_username}/`;
+                            let profileThumbUrl = `//uploads.scratch.mit.edu/users/avatars/${item.actor_id}.png`;
                             if (item.type === 'becomeownerstudio') {
-                                profileLink = '/users/' + item.recipient_username; + '/';
-                                profileThumbUrl = '//uploads.scratch.mit.edu/users/avatars/'
-                                    + item.recipient_id
-                                    + '.png';
+                                profileLink = `/users/${item.recipient_username}/`;
+                                profileThumbUrl = `//uploads.scratch.mit.edu/users/avatars/${item.recipient_id}.png`;
                             }
 
                             return (
-                                <li className="activity-li">
+                                <li
+                                    className="activity-li"
+                                    key={`${item.type}_${item.datetime_created}`}
+                                >
                                     <a href={profileLink}>
                                         <img
                                             alt=""
@@ -140,88 +163,89 @@ var ActivityList = injectIntl(React.createClass({
                                     {this.getComponentForMessage(item)}
                                 </li>
                             );
-                        }.bind(this))}
+                        })}
                     </ul>
                 ] : [
-                    <div className="empty" key="activity-empty">
+                    <div
+                        className="empty"
+                        key="activity-empty"
+                    >
                         <h4>
                             <FormattedMessage
+                                defaultMessage="This is where you will see updates from Scratchers you follow"
                                 id="activity.seeUpdates"
-                                defaultMessage="This is where you will see updates from Scratchers you follow" />
+                            />
                         </h4>
                         <a href="/studios/146521/">
                             <FormattedMessage
+                                defaultMessage="Check out some Scratchers you might like to follow"
                                 id="activity.checkOutScratchers"
-                                defaultMessage="Check out some Scratchers you might like to follow" />
+                            />
                         </a>
                     </div>
                 ]}
             </Box>
         );
     }
-}));
+}
 
-var SplashPresentation = injectIntl(React.createClass({
-    type: 'Splash',
-    propTypes: {
-        sessionStatus: React.PropTypes.string.isRequired,
-        user: React.PropTypes.object.isRequired,
-        isEducator: React.PropTypes.bool.isRequired,
-        isAdmin: React.PropTypes.bool.isRequired,
-        handleDismiss: React.PropTypes.func.isRequired,
-        refreshHomepageCache: React.PropTypes.func.isRequired,
-        shouldShowEmailConfirmation: React.PropTypes.bool.isRequired,
-        emailConfirmationModalOpen: React.PropTypes.bool.isRequired,
-        showEmailConfirmationModal: React.PropTypes.func.isRequired,
-        hideEmailConfirmationModal: React.PropTypes.func.isRequired,
-        shouldShowWelcome: React.PropTypes.bool.isRequired,
-        refreshCacheStatus: React.PropTypes.object.isRequired
-    },
-    getDefaultProps: function () {
-        return {
-            projectCount: 20000000, // gets the shared project count
-            activity: [], // recent social actions taken by users someone is following
-            news: [], // gets news posts from the scratch Tumblr
-            sharedByFollowing: [], // "Projects by Scratchers I'm Following"
-            lovedByFollowing: [], // "Projects Loved by Scratchers I'm Following"
-            inStudiosFollowing: [], // "Projects in Studios I'm Following"
-            featuredGlobal: {} // global homepage rows, such as "Featured Projects"
-        };
-    },
-    componentDidMount: function () {
-        if (this.props.shouldShowEmailConfirmation) window.addEventListener('message', this.onMessage);
-    },
-    componentWillUnmount: function () {
-        window.removeEventListener('message', this.onMessage);
-    },
-    onMessage: function (e) {
-        if (e.origin != window.location.origin) return;
-        if (e.source != this.emailConfirmationiFrame.contentWindow) return;
-        if (e.data == 'resend-done') {
-            this.hideEmailConfirmationModal();
+ActivityList.propTypes = {
+    intl: intlShape,
+    items: PropTypes.arrayOf(PropTypes.object)
+};
+
+const WrappedActivityList = injectIntl(ActivityList);
+
+// Splash page
+class SplashPresentation extends React.Component { // eslint-disable-line react/no-multi-comp
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleMessage',
+            'renderHomepageRows'
+        ]);
+    }
+    componentDidMount () {
+        if (this.props.shouldShowEmailConfirmation) window.addEventListener('message', this.handleMessage);
+    }
+    componentWillUnmount () {
+        window.removeEventListener('message', this.handleMessage);
+    }
+    handleMessage (e) {
+        if (e.origin !== window.location.origin) return;
+        if (e.source !== this.emailConfirmationiFrame.contentWindow) return;
+        if (e.data === 'resend-done') {
+            this.props.onHideEmailConfirmationModal();
         } else {
-            var data = JSON.parse(e.data);
-            if (data['action'] === 'leave-page') {
-                window.location.href = data['uri'];
+            const data = JSON.parse(e.data);
+            if (data.action === 'leave-page') {
+                window.location.href = data.uri;
             }
         }
-    },
-    renderHomepageRows: function () {
-        var formatMessage = this.props.intl.formatMessage;
-        var rows = [
+    }
+    renderHomepageRows () {
+        const rows = [
             <Box
-                title={formatMessage({id: 'splash.featuredProjects'})}
                 key="community_featured_projects"
+                title={this.props.intl.formatMessage({
+                    id: 'splash.featuredProjects'
+                })}
             >
                 <LegacyCarousel items={this.props.featuredGlobal.community_featured_projects} />
             </Box>,
             <Box
-                title={formatMessage({id: 'splash.featuredStudios'})}
                 key="community_featured_studios"
+                title={this.props.intl.formatMessage({
+                    id: 'splash.featuredStudios'
+                })}
             >
                 <LegacyCarousel
                     items={this.props.featuredGlobal.community_featured_studios}
-                    settings={{slidesToShow: 4, slidesToScroll: 4, lazyLoad: false}}
+                    settings={{
+                        slidesToShow: 4,
+                        slidesToScroll: 4,
+                        lazyLoad: false
+                    }}
                 />
             </Box>
         ];
@@ -229,14 +253,17 @@ var SplashPresentation = injectIntl(React.createClass({
         if (this.props.featuredGlobal.curator_top_projects &&
             this.props.featuredGlobal.curator_top_projects.length > 4) {
 
+            const curatorName = this.props.featuredGlobal.curator_top_projects[0].curator_name;
             rows.push(
                 <Box
                     key="curator_top_projects"
-                    title={
-                        formatMessage({id: 'splash.projectsCuratedBy'}) + ' ' +
-                        this.props.featuredGlobal.curator_top_projects[0].curator_name}
-                    moreTitle={formatMessage({id: 'general.learnMore'})}
                     moreHref="/studios/386359/"
+                    moreTitle={this.props.intl.formatMessage({
+                        id: 'general.learnMore'
+                    })}
+                    title={
+                        `${this.props.intl.formatMessage({id: 'splash.projectsCuratedBy'})}' '${curatorName}`
+                    }
                 >
                     <LegacyCarousel items={this.props.featuredGlobal.curator_top_projects} />
                 </Box>
@@ -246,14 +273,15 @@ var SplashPresentation = injectIntl(React.createClass({
         if (this.props.featuredGlobal.scratch_design_studio &&
             this.props.featuredGlobal.scratch_design_studio.length > 4) {
 
+            const galleryTitle = this.props.featuredGlobal.scratch_design_studio[0].gallery_title;
             rows.push(
                 <Box
                     key="scratch_design_studio"
+                    moreHref={`/studios/${this.props.featuredGlobal.scratch_design_studio[0].gallery_id}/`}
+                    moreTitle={this.props.intl.formatMessage({id: 'splash.visitTheStudio'})}
                     title={
-                        formatMessage({id: 'splash.scratchDesignStudioTitle'})
-                        + ' - ' + this.props.featuredGlobal.scratch_design_studio[0].gallery_title}
-                    moreTitle={formatMessage({id: 'splash.visitTheStudio'})}
-                    moreHref={'/studios/' + this.props.featuredGlobal.scratch_design_studio[0].gallery_id + '/'}
+                        `${this.props.intl.formatMessage({id: 'splash.scratchDesignStudioTitle'})} - ${galleryTitle}`
+                    }
                 >
                     <LegacyCarousel items={this.props.featuredGlobal.scratch_design_studio} />
                 </Box>
@@ -266,8 +294,8 @@ var SplashPresentation = injectIntl(React.createClass({
 
             rows.push(
                 <Box
-                    title={formatMessage({id: 'splash.recentlySharedProjects'})}
                     key="community_newest_projects"
+                    title={this.props.intl.formatMessage({id: 'splash.recentlySharedProjects'})}
                 >
                     <LegacyCarousel items={this.props.featuredGlobal.community_newest_projects} />
                 </Box>
@@ -277,8 +305,8 @@ var SplashPresentation = injectIntl(React.createClass({
         if (this.props.sharedByFollowing && this.props.sharedByFollowing.length > 0) {
             rows.push(
                 <Box
-                    title={formatMessage({id: 'splash.projectsByScratchersFollowing'})}
                     key="custom_projects_by_following"
+                    title={this.props.intl.formatMessage({id: 'splash.projectsByScratchersFollowing'})}
                 >
                     <Carousel items={this.props.sharedByFollowing} />
                 </Box>
@@ -288,8 +316,8 @@ var SplashPresentation = injectIntl(React.createClass({
         if (this.props.lovedByFollowing && this.props.lovedByFollowing.length > 0) {
             rows.push(
                 <Box
-                    title={formatMessage({id: 'splash.projectsLovedByScratchersFollowing'})}
                     key="custom_projects_loved_by_following"
+                    title={this.props.intl.formatMessage({id: 'splash.projectsLovedByScratchersFollowing'})}
                 >
                     <Carousel items={this.props.lovedByFollowing} />
                 </Box>
@@ -299,8 +327,8 @@ var SplashPresentation = injectIntl(React.createClass({
         if (this.props.inStudiosFollowing && this.props.inStudiosFollowing.length > 0) {
             rows.push(
                 <Box
-                    title={formatMessage({id:'splash.projectsInStudiosFollowing'})}
                     key="custom_projects_in_studios_following"
+                    title={this.props.intl.formatMessage({id: 'splash.projectsInStudiosFollowing'})}
                 >
                     <Carousel items={this.props.inStudiosFollowing} />
                 </Box>
@@ -309,34 +337,34 @@ var SplashPresentation = injectIntl(React.createClass({
 
         rows.push(
             <Box
-                title={formatMessage({id: 'splash.communityRemixing'})}
                 key="community_most_remixed_projects"
+                title={this.props.intl.formatMessage({id: 'splash.communityRemixing'})}
             >
                 <LegacyCarousel
+                    showRemixes
                     items={shuffle(this.props.featuredGlobal.community_most_remixed_projects)}
-                    showRemixes={true}
                 />
             </Box>,
             <Box
-                title={formatMessage({id: 'splash.communityLoving'})}
                 key="community_most_loved_projects"
+                title={this.props.intl.formatMessage({id: 'splash.communityLoving'})}
             >
                 <LegacyCarousel
+                    showLoves
                     items={shuffle(this.props.featuredGlobal.community_most_loved_projects)}
-                    showLoves={true}
                 />
             </Box>
         );
 
         return rows;
-    },
-    render: function () {
-        var featured = this.renderHomepageRows();
+    }
+    render () {
+        const featured = this.renderHomepageRows();
 
-        var formatHTMLMessage = this.props.intl.formatHTMLMessage;
-        var formatNumber = this.props.intl.formatNumber;
-        var formatMessage = this.props.intl.formatMessage;
-        var messages = {
+        const formatHTMLMessage = this.props.intl.formatHTMLMessage;
+        const formatNumber = this.props.intl.formatNumber;
+        const formatMessage = this.props.intl.formatMessage;
+        const messages = {
             'general.viewAll': formatMessage({id: 'general.viewAll'}),
             'news.scratchNews': formatMessage({id: 'news.scratchNews'}),
             'welcome.welcomeToScratch': formatMessage({id: 'welcome.welcomeToScratch'}),
@@ -360,7 +388,7 @@ var SplashPresentation = injectIntl(React.createClass({
         if (this.props.projectCount === 20000000) {
             messages['intro.description'] = formatHTMLMessage({id: 'intro.defaultDescription'});
         } else {
-            var count = formatNumber(this.props.projectCount);
+            const count = formatNumber(this.props.projectCount);
             messages['intro.description'] = formatHTMLMessage({id: 'intro.description'}, {value: count});
         }
 
@@ -368,47 +396,78 @@ var SplashPresentation = injectIntl(React.createClass({
             <div className="splash">
                 {this.props.shouldShowEmailConfirmation ? [
                     <DropdownBanner
-                        key="confirmedEmail"
                         className="warning"
-                        onRequestDismiss={this.props.handleDismiss.bind(this, 'confirmed_email')}
+                        key="confirmedEmail"
+                        onRequestDismiss={() => { // eslint-disable-line react/jsx-no-bind
+                            this.props.onDismiss('confirmed_email');
+                        }}
                     >
-                        <a href="#" onClick={this.props.showEmailConfirmationModal}>Confirm your email</a>
-                        {' '}to enable sharing.{' '}
-                        <a href="/info/faq/#accounts">Having trouble?</a>
+                        <a
+                            href="#"
+                            onClick={this.props.onShowEmailConfirmationModal}
+                        >
+                            Confirm your email
+                        </a>{' '}to enable sharing.{' '}
+                        <a href="/info/faq/#accounts">
+                            Having trouble?
+                        </a>
                     </DropdownBanner>,
                     <IframeModal
-                        isOpen={this.props.emailConfirmationModalOpen}
-                        onRequestClose={this.props.hideEmailConfirmationModal}
                         className="mod-confirmation"
-                        componentRef={
-                            function (iframe) {
-                                this.emailConfirmationiFrame = iframe;
-                            }.bind(this)
-                        }
+                        componentRef={iframe => { // eslint-disable-line react/jsx-no-bind
+                            this.emailConfirmationiFrame = iframe;
+                        }}
+                        isOpen={this.props.emailConfirmationModalOpen}
+                        key="iframe-modal"
                         src="/accounts/email_resend_standalone/"
+                        onRequestClose={this.props.onHideEmailConfirmationModal}
                     />
                 ] : []}
                 {this.props.isEducator ? [
-                    <TeacherBanner key="teacherbanner" messages={messages} />
+                    <TeacherBanner
+                        key="teacherbanner"
+                        messages={messages}
+                    />
                 ] : []}
-                <div key="inner" className="inner mod-splash">
+                <div
+                    className="inner mod-splash"
+                    key="inner"
+                >
                     {this.props.sessionStatus === sessionActions.Status.FETCHED ? (
-                        Object.keys(this.props.user).length !== 0 ? [
-                            <div key="header" className="splash-header">
+                        Object.keys(this.props.user).length > 0 ? [
+                            <div
+                                className="splash-header"
+                                key="header"
+                            >
                                 {this.props.shouldShowWelcome ? [
                                     <Welcome
                                         key="welcome"
-                                        onDismiss={this.props.handleDismiss.bind(this, 'welcome')}
                                         messages={messages}
+                                        onDismiss={() => { // eslint-disable-line react/jsx-no-bind
+                                            this.props.onDismiss('welcome');
+                                        }}
                                     />
                                 ] : [
-                                    <ActivityList key="activity" items={this.props.activity} />
+                                    <WrappedActivityList
+                                        items={this.props.activity}
+                                        key="activity"
+                                    />
                                 ]}
-                                <News items={this.props.news} messages={messages} />
+                                <News
+                                    items={this.props.news}
+                                    messages={messages}
+                                />
                             </div>
                         ] : [
-                            <MediaQuery minWidth={frameless.desktop}>
-                                <Intro projectCount={this.props.projectCount} messages={messages} key="intro"/>
+                            <MediaQuery
+                                key="frameless-desktop"
+                                minWidth={frameless.desktop}
+                            >
+                                <Intro
+                                    key="intro"
+                                    messages={messages}
+                                    projectCount={this.props.projectCount}
+                                />
                             </MediaQuery>
                         ]) : []
                     }
@@ -416,7 +475,7 @@ var SplashPresentation = injectIntl(React.createClass({
                     {featured}
 
                     {this.props.isAdmin ? [
-                        <AdminPanel>
+                        <AdminPanel key="admin-panel">
                             <dt>Tools</dt>
                             <dd>
                                 <ul>
@@ -437,9 +496,11 @@ var SplashPresentation = injectIntl(React.createClass({
                                     <li>
                                         <div className="button-row">
                                             <span>Refresh row data:</span>
-                                            <Button onClick={this.props.refreshHomepageCache}
-                                                    className={this.props.refreshCacheStatus.status}
-                                                    disabled={this.props.refreshCacheStatus.disabled}>
+                                            <Button
+                                                className={this.props.refreshCacheStatus.status}
+                                                disabled={this.props.refreshCacheStatus.disabled}
+                                                onClick={this.props.onRefreshHomepageCache}
+                                            >
                                                 <span>{this.props.refreshCacheStatus.content}</span>
                                             </Button>
                                         </div>
@@ -452,6 +513,47 @@ var SplashPresentation = injectIntl(React.createClass({
             </div>
         );
     }
-}));
+}
 
-module.exports = SplashPresentation;
+SplashPresentation.propTypes = {
+    activity: PropTypes.arrayOf(PropTypes.object),
+    emailConfirmationModalOpen: PropTypes.bool.isRequired,
+    featuredGlobal: PropTypes.shape({
+        community_featured_projects: PropTypes.array,
+        community_featured_studios: PropTypes.array,
+        curator_top_projects: PropTypes.array,
+        scratch_design_studio: PropTypes.array,
+        community_newest_projects: PropTypes.array,
+        community_most_remixed_projects: PropTypes.array,
+        community_most_loved_projects: PropTypes.array
+    }),
+    inStudiosFollowing: PropTypes.arrayOf(PropTypes.object),
+    intl: intlShape,
+    isAdmin: PropTypes.bool.isRequired,
+    isEducator: PropTypes.bool.isRequired,
+    lovedByFollowing: PropTypes.arrayOf(PropTypes.object),
+    news: PropTypes.object, // eslint-disable-line react/forbid-prop-types
+    onDismiss: PropTypes.func.isRequired,
+    onHideEmailConfirmationModal: PropTypes.func.isRequired,
+    onRefreshHomepageCache: PropTypes.func.isRequired,
+    onShowEmailConfirmationModal: PropTypes.func.isRequired,
+    projectCount: PropTypes.number,
+    refreshCacheStatus: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
+    sessionStatus: PropTypes.string.isRequired,
+    sharedByFollowing: PropTypes.arrayOf(PropTypes.object),
+    shouldShowEmailConfirmation: PropTypes.bool.isRequired,
+    shouldShowWelcome: PropTypes.bool.isRequired,
+    user: PropTypes.object.isRequired // eslint-disable-line react/forbid-prop-types
+};
+
+SplashPresentation.defaultProps = {
+    activity: [], // recent social actions taken by users someone is following
+    featuredGlobal: {}, // global homepage rows, such as "Featured Projects"
+    inStudiosFollowing: [], // "Projects in Studios I'm Following"
+    lovedByFollowing: [], // "Projects Loved by Scratchers I'm Following"
+    news: [], // gets news posts from the scratch Tumblr
+    projectCount: 20000000, // gets the shared project count
+    sharedByFollowing: [] // "Projects by Scratchers I'm Following"
+};
+
+module.exports = injectIntl(SplashPresentation);
diff --git a/src/views/splash/splash.jsx b/src/views/splash/splash.jsx
index 85e74717e..59c1b6d51 100644
--- a/src/views/splash/splash.jsx
+++ b/src/views/splash/splash.jsx
@@ -1,37 +1,52 @@
-var connect = require('react-redux').connect;
-var injectIntl = require('react-intl').injectIntl;
-var React = require('react');
+const bindAll = require('lodash.bindall');
+const connect = require('react-redux').connect;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var api = require('../../lib/api');
-var log = require('../../lib/log');
-var render = require('../../lib/render.jsx');
-var sessionActions = require('../../redux/session.js');
-var splashActions = require('../../redux/splash.js');
+const api = require('../../lib/api');
+const log = require('../../lib/log');
+const render = require('../../lib/render.jsx');
+const sessionActions = require('../../redux/session.js');
+const splashActions = require('../../redux/splash.js');
 
-var Page = require('../../components/page/www/page.jsx');
-var SplashPresentation = require('./presentation.jsx');
+const Page = require('../../components/page/www/page.jsx');
+const SplashPresentation = require('./presentation.jsx');
 
-var Splash = injectIntl(React.createClass({
-    type: 'Splash',
-    getInitialState: function () {
-        return {
+class Splash extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'getNews',
+            'getProjectCount',
+            'handleRefreshHomepageCache',
+            'getHomepageRefreshStatus',
+            'handleShowEmailConfirmationModal',
+            'handleHideEmailConfirmationModal',
+            'handleDismiss',
+            'shouldShowWelcome',
+            'shouldShowEmailConfirmation'
+        ]);
+        this.state = {
             projectCount: 20000000, // gets the shared project count
             news: [], // gets news posts from the scratch Tumblr
             emailConfirmationModalOpen: false, // flag that determines whether to show banner to request email conf.
             refreshCacheStatus: 'notrequested'
         };
-    },
-    getDefaultProps: function () {
-        return {
-            sessionStatus: sessionActions.Status.NOT_FETCHED,
-            user: {},
-            flags: {},
-            isEducator: false,
-            isAdmin: false
-        };
-    },
-    componentDidUpdate: function (prevProps) {
-        if (this.props.user != prevProps.user) {
+    }
+    componentDidMount () {
+        this.props.getFeaturedGlobal();
+        if (this.props.user.username) {
+            this.props.getActivity(this.props.user.username, this.props.user.token);
+            this.props.getSharedByFollowing(this.props.user.username, this.props.user.token);
+            this.props.getInStudiosFollowing(this.props.user.username, this.props.user.token);
+            this.props.getLovedByFollowing(this.props.user.username, this.props.user.token);
+            this.getNews();
+        } else {
+            this.getProjectCount();
+        }
+    }
+    componentDidUpdate (prevProps) {
+        if (this.props.user.username !== prevProps.user.username) {
             if (this.props.user.username) {
                 this.props.getActivity(this.props.user.username, this.props.user.token);
                 this.props.getSharedByFollowing(this.props.user.username, this.props.user.token);
@@ -43,7 +58,7 @@ var Splash = injectIntl(React.createClass({
                 this.props.setRows('loved', []);
                 this.props.setRows('studios', []);
                 this.props.setRows('activity', []);
-                this.setState({news: []});
+                this.setState({news: []}); // eslint-disable-line react/no-did-update-set-state
                 this.getProjectCount();
             }
             if (this.shouldShowEmailConfirmation()) {
@@ -52,50 +67,38 @@ var Splash = injectIntl(React.createClass({
                 window.removeEventListener('message', this.onMessage);
             }
         }
-    },
-    componentDidMount: function () {
-        this.props.getFeaturedGlobal();
-        if (this.props.user.username) {
-            this.props.getActivity(this.props.user.username, this.props.user.token);
-            this.props.getSharedByFollowing(this.props.user.username, this.props.user.token);
-            this.props.getInStudiosFollowing(this.props.user.username, this.props.user.token);
-            this.props.getLovedByFollowing(this.props.user.username, this.props.user.token);
-            this.getNews();
-        } else {
-            this.getProjectCount();
-        }
-    },
-    getNews: function () {
+    }
+    getNews () {
         api({
             uri: '/news?limit=3'
-        }, function (err, body) {
+        }, (err, body) => {
             if (!body) return log.error('No response body');
             if (!err) return this.setState({news: body});
-        }.bind(this));
-    },
-    getProjectCount: function () {
+        });
+    }
+    getProjectCount () {
         api({
             uri: '/projects/count/all'
-        }, function (err, body) {
+        }, (err, body) => {
             if (!body) return log.error('No response body');
             if (!err) return this.setState({projectCount: body.count});
-        }.bind(this));
-    },
-    refreshHomepageCache: function () {
+        });
+    }
+    handleRefreshHomepageCache () {
         api({
             host: '',
             uri: '/scratch_admin/homepage/clear-cache/',
             method: 'post',
             useCsrf: true
-        }, function (err, body) {
+        }, (err, body) => {
             if (err) return this.setState({refreshCacheStatus: 'fail'});
             if (!body) return log.error('No response body');
             if (!body.success) return this.setState({refreshCacheStatus: 'inprogress'});
             return this.setState({refreshCacheStatus: 'pass'});
-        }.bind(this));
-    },
-    getHomepageRefreshStatus: function () {
-        var status = {
+        });
+    }
+    getHomepageRefreshStatus () {
+        const status = {
             status: this.state.refreshCacheStatus,
             disabled: false,
             content: 'Refresh'
@@ -106,111 +109,160 @@ var Splash = injectIntl(React.createClass({
         } else if (this.state.refreshCacheStatus === 'pass') {
             status.disabled = true;
             status.content = 'Requested';
-        } else if (this.state.refreshCacheStatus == 'fail') {
+        } else if (this.state.refreshCacheStatus === 'fail') {
             status.disabled = false;
             status.content = 'Error';
         }
         return status;
-    },
-    showEmailConfirmationModal: function () {
+    }
+    handleShowEmailConfirmationModal () {
         this.setState({emailConfirmationModalOpen: true});
-    },
-    hideEmailConfirmationModal: function () {
+    }
+    handleHideEmailConfirmationModal () {
         this.setState({emailConfirmationModalOpen: false});
-    },
-    handleDismiss: function (cue) {
+    }
+    handleDismiss (cue) {
         api({
             host: '',
             uri: '/site-api/users/set-template-cue/',
             method: 'post',
             useCsrf: true,
             json: {cue: cue, value: false}
-        }, function (err) {
+        }, err => {
             if (!err) this.props.dispatch(sessionActions.refreshSession());
-        }.bind(this));
-    },
-    shouldShowWelcome: function () {
+        });
+    }
+    shouldShowWelcome () {
         if (!this.props.user || !this.props.flags.show_welcome) return false;
         return (
             new Date(this.props.user.dateJoined) >
-            new Date(new Date - 2*7*24*60*60*1000) // Two weeks ago
+            new Date(new Date() - (2 * 7 * 24 * 60 * 60 * 1000)) // Two weeks ago
         );
-    },
-    shouldShowEmailConfirmation: function () {
+    }
+    shouldShowEmailConfirmation () {
         return (
             this.props.user && this.props.flags.has_outstanding_email_confirmation &&
-            this.props.flags.confirm_email_banner);
-    },
-    render: function () {
-        var showEmailConfirmation = this.shouldShowEmailConfirmation() || false;
-        var showWelcome = this.shouldShowWelcome();
-        var homepageRefreshStatus = this.getHomepageRefreshStatus();
+            this.props.flags.confirm_email_banner
+        );
+    }
+    render () {
+        const showEmailConfirmation = this.shouldShowEmailConfirmation() || false;
+        const showWelcome = this.shouldShowWelcome();
+        const homepageRefreshStatus = this.getHomepageRefreshStatus();
 
         return (
             <SplashPresentation
-                sessionStatus={this.props.sessionStatus}
-                user={this.props.user}
-                isEducator={this.props.isEducator}
-                isAdmin={this.props.isAdmin}
-                handleDismiss={this.handleDismiss}
-                refreshHomepageCache={this.refreshHomepageCache}
-                shouldShowEmailConfirmation={showEmailConfirmation}
-                emailConfirmationModalOpen={this.state.emailConfirmationModalOpen}
-                showEmailConfirmationModal={this.showEmailConfirmationModal}
-                hideEmailConfirmationModal={this.hideEmailConfirmationModal}
-                shouldShowWelcome={showWelcome}
-                projectCount={this.state.projectCount}
                 activity={this.props.activity}
-                news={this.state.news}
-                sharedByFollowing={this.props.shared}
-                lovedByFollowing={this.props.loved}
-                inStudiosFollowing={this.props.studios}
+                emailConfirmationModalOpen={this.state.emailConfirmationModalOpen}
                 featuredGlobal={this.props.featured}
+                inStudiosFollowing={this.props.studios}
+                isAdmin={this.props.isAdmin}
+                isEducator={this.props.isEducator}
+                lovedByFollowing={this.props.loved}
+                news={this.state.news}
+                projectCount={this.state.projectCount}
                 refreshCacheStatus={homepageRefreshStatus}
+                sessionStatus={this.props.sessionStatus}
+                sharedByFollowing={this.props.shared}
+                shouldShowEmailConfirmation={showEmailConfirmation}
+                shouldShowWelcome={showWelcome}
+                user={this.props.user}
+                onDismiss={this.handleDismiss}
+                onHideEmailConfirmationModal={this.handleHideEmailConfirmationModal}
+                onRefreshHomepageCache={this.handleRefreshHomepageCache}
+                onShowEmailConfirmationModal={this.handleShowEmailConfirmationModal}
             />
         );
     }
-}));
+}
 
-var mapStateToProps = function (state) {
-    return {
-        sessionStatus: state.session.status,
-        user: state.session.session.user,
-        flags: state.session.session.flags,
-        isEducator: state.permissions.educator,
-        isAdmin: state.permissions.admin,
-        activity: state.splash.activity.rows,
-        featured: state.splash.featured.rows,
-        shared: state.splash.shared.rows,
-        loved: state.splash.loved.rows,
-        studios: state.splash.studios.rows
-    };
+Splash.propTypes = {
+    activity: PropTypes.arrayOf(PropTypes.object).isRequired,
+    dispatch: PropTypes.func,
+    featured: PropTypes.shape({
+        community_featured_projects: PropTypes.array,
+        community_featured_studios: PropTypes.array,
+        curator_top_projects: PropTypes.array,
+        scratch_design_studio: PropTypes.array,
+        community_newest_projects: PropTypes.array,
+        community_most_remixed_projects: PropTypes.array,
+        community_most_loved_projects: PropTypes.array
+    }),
+    flags: PropTypes.shape({
+        must_reset_password: PropTypes.bool,
+        must_complete_registration: PropTypes.bool,
+        has_outstanding_email_confirmation: PropTypes.bool,
+        show_welcome: PropTypes.bool,
+        confirm_email_banner: PropTypes.bool,
+        unsupported_browser_banner: PropTypes.bool
+    }),
+    getActivity: PropTypes.func.isRequired,
+    getFeaturedGlobal: PropTypes.func.isRequired,
+    getInStudiosFollowing: PropTypes.func.isRequired,
+    getLovedByFollowing: PropTypes.func.isRequired,
+    getSharedByFollowing: PropTypes.func.isRequired,
+    isAdmin: PropTypes.bool,
+    isEducator: PropTypes.bool,
+    loved: PropTypes.arrayOf(PropTypes.object).isRequired,
+    sessionStatus: PropTypes.string,
+    setRows: PropTypes.func.isRequired,
+    shared: PropTypes.arrayOf(PropTypes.object).isRequired,
+    studios: PropTypes.arrayOf(PropTypes.object).isRequired,
+    user: PropTypes.shape({
+        id: PropTypes.number,
+        banned: PropTypes.bool,
+        username: PropTypes.string,
+        token: PropTypes.string,
+        thumbnailUrl: PropTypes.string,
+        dateJoined: PropTypes.string,
+        email: PropTypes.string,
+        classroomId: PropTypes.string
+    })
 };
 
-var mapDispatchToProps = function (dispatch) {
-    return {
-        getFeaturedGlobal: function () {
-            dispatch(splashActions.getFeaturedGlobal());
-        },
-        getActivity: function (username, token) {
-            dispatch(splashActions.getActivity(username, token));
-        },
-        getSharedByFollowing: function (username, token) {
-            dispatch(splashActions.getSharedByFollowing(username, token));
-        },
-        getInStudiosFollowing: function (username, token) {
-            dispatch(splashActions.getInStudiosFollowing(username, token));
-        },
-        getLovedByFollowing: function (username, token) {
-            dispatch(splashActions.getLovedByFollowing(username, token));
-        },
-        setRows: function (type, rows) {
-            dispatch(splashActions.setRows(type, rows));
-        }
-    };
+Splash.defaultProps = {
+    flags: {},
+    isAdmin: false,
+    isEducator: false,
+    sessionStatus: sessionActions.Status.NOT_FETCHED,
+    user: {}
 };
 
-var ConnectedSplash = connect(
+const mapStateToProps = state => ({
+    activity: state.splash.activity.rows,
+    featured: state.splash.featured.rows,
+    flags: state.session.session.flags,
+    isAdmin: state.permissions.admin,
+    isEducator: state.permissions.educator,
+    loved: state.splash.loved.rows,
+    sessionStatus: state.session.status,
+    shared: state.splash.shared.rows,
+    studios: state.splash.studios.rows,
+    user: state.session.session.user
+});
+
+const mapDispatchToProps = dispatch => ({
+    getFeaturedGlobal: () => {
+        dispatch(splashActions.getFeaturedGlobal());
+    },
+    getActivity: (username, token) => {
+        dispatch(splashActions.getActivity(username, token));
+    },
+    getSharedByFollowing: (username, token) => {
+        dispatch(splashActions.getSharedByFollowing(username, token));
+    },
+    getInStudiosFollowing: (username, token) => {
+        dispatch(splashActions.getInStudiosFollowing(username, token));
+    },
+    getLovedByFollowing: (username, token) => {
+        dispatch(splashActions.getLovedByFollowing(username, token));
+    },
+    setRows: (type, rows) => {
+        dispatch(splashActions.setRows(type, rows));
+    }
+});
+
+const ConnectedSplash = connect(
     mapStateToProps,
     mapDispatchToProps
 )(Splash);
diff --git a/src/views/studentcompleteregistration/studentcompleteregistration.jsx b/src/views/studentcompleteregistration/studentcompleteregistration.jsx
index e337470d7..6724ac87b 100644
--- a/src/views/studentcompleteregistration/studentcompleteregistration.jsx
+++ b/src/views/studentcompleteregistration/studentcompleteregistration.jsx
@@ -1,44 +1,47 @@
-var connect = require('react-redux').connect;
-var defaults = require('lodash.defaultsdeep');
-var React = require('react');
-var render = require('../../lib/render.jsx');
+const bindAll = require('lodash.bindall');
+const connect = require('react-redux').connect;
+const defaults = require('lodash.defaultsdeep');
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var sessionStatus = require('../../redux/session').Status;
-var api = require('../../lib/api');
-var intl = require('../../lib/intl.jsx');
-var log = require('../../lib/log.js');
+const api = require('../../lib/api');
+const injectIntl = require('../../lib/intl.jsx').injectIntl;
+const intlShape = require('../../lib/intl.jsx').intlShape;
+const log = require('../../lib/log.js');
+const sessionStatus = require('../../redux/session').Status;
 
-var Deck = require('../../components/deck/deck.jsx');
-var Progression = require('../../components/progression/progression.jsx');
-var Spinner = require('../../components/spinner/spinner.jsx');
-var Steps = require('../../components/registration/steps.jsx');
+const Deck = require('../../components/deck/deck.jsx');
+const Progression = require('../../components/progression/progression.jsx');
+const Spinner = require('../../components/spinner/spinner.jsx');
+const Steps = require('../../components/registration/steps.jsx');
+
+const render = require('../../lib/render.jsx');
 
 require('./studentcompleteregistration.scss');
 
-var StudentCompleteRegistration = intl.injectIntl(React.createClass({
-    type: 'StudentCompleteRegistration',
-    getInitialState: function () {
-        return {
+class StudentCompleteRegistration extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleAdvanceStep',
+            'handleLogOut',
+            'handleRegister',
+            'handleGoToClass'
+        ]);
+        this.state = {
             classroom: null,
             formData: {},
             registrationErrors: null,
             step: 0,
             waiting: false
         };
-    },
-    advanceStep: function (formData) {
-        formData = formData || {};
-        this.setState({
-            step: this.state.step + 1,
-            formData: defaults({}, formData, this.state.formData)
-        });
-    },
-    componentDidUpdate: function (prevProps) {
+    }
+    componentDidUpdate (prevProps) {
         if (prevProps.studentUsername !== this.props.studentUsername && this.props.newStudent) {
-            this.setState({waiting: true});
+            this.setState({waiting: true}); // eslint-disable-line react/no-did-update-set-state
             api({
-                uri: '/classrooms/' + this.props.classroomId
-            }, function (err, body, res) {
+                uri: `/classrooms/${this.props.classroomId}`
+            }, (err, body, res) => {
                 this.setState({waiting: false});
                 if (err || res.statusCode !== 200) {
                     return this.setState({
@@ -48,81 +51,90 @@ var StudentCompleteRegistration = intl.injectIntl(React.createClass({
                     });
                 }
                 this.setState({classroom: body});
-            }.bind(this));
+            });
         }
-    },
-    handleLogOut: function (e) {
+    }
+    handleAdvanceStep (formData) {
+        formData = formData || {};
+        this.setState({
+            step: this.state.step + 1,
+            formData: defaults({}, formData, this.state.formData)
+        });
+    }
+    handleLogOut (e) {
         e.preventDefault();
         api({
             host: '',
             method: 'post',
             uri: '/accounts/logout/',
             useCsrf: true
-        }, function (err) {
+        }, err => {
             if (err) return log.error(err);
             window.location = '/';
-        }.bind(this));
-    },
-    register: function (formData) {
+        });
+    }
+    handleRegister (formData) {
         this.setState({waiting: true});
+        
         formData = defaults({}, formData || {}, this.state.formData);
-        var submittedData = {
+        const submittedData = {
             birth_month: formData.user.birth.month,
             birth_year: formData.user.birth.year,
-            gender: (
-                formData.user.gender === 'other' ?
-                formData.user.genderOther :
-                formData.user.gender
-            ),
+            gender: (formData.user.gender === 'other' ? formData.user.genderOther : formData.user.gender),
             country: formData.user.country,
             is_robot: formData.user.isRobot
         };
         if (this.props.must_reset_password) {
             submittedData.password = formData.user.password;
         }
+        
         api({
             host: '',
             uri: '/classes/student_update_registration/',
             method: 'post',
             useCsrf: true,
             formData: submittedData
-        }, function (err, body, res) {
+        }, (err, body, res) => {
             this.setState({waiting: false});
             if (err) return this.setState({registrationError: err});
-            if (body.success) return this.advanceStep(formData);
+            if (body.success) return this.handleAdvanceStep(formData);
             this.setState({
-                registrationErrors:
-                    body.errors || {
-                        __all__:
-                            this.props.intl.formatMessage({id: 'registration.generalError'}) +
-                            ' (' + res.statusCode + ')'
-                    }
+                registrationErrors: body.errors || {
+                    __all__:
+                        `${this.props.intl.formatMessage({id: 'registration.generalError'})} (${res.statusCode})`
+                }
             });
-        }.bind(this));
-    },
-    goToClass: function () {
-        window.location = '/classes/' + this.state.classroom.id + '/';
-    },
-    render: function () {
-        var demographicsDescription = this.props.intl.formatMessage({
-            id: 'registration.studentPersonalStepDescription'});
-        var registrationErrors = this.state.registrationErrors;
+        });
+    }
+    handleGoToClass () {
+        window.location = `/classes/${this.state.classroom.id}/`;
+    }
+    render () {
+        const demographicsDescription = this.props.intl.formatMessage({
+            id: 'registration.studentPersonalStepDescription'
+        });
+        let registrationErrors = this.state.registrationErrors;
         if (!this.props.newStudent) {
             registrationErrors = {
                 __all__: this.props.intl.formatMessage({id: 'registration.mustBeNewStudent'})
             };
         }
+
         return (
             <Deck className="student-registration">
                 {registrationErrors ? (
                     <Steps.RegistrationError>
                         <ul>
-                            {Object.keys(registrationErrors).map(function (field) {
-                                var label = field + ': ';
+                            {Object.keys(registrationErrors).map(field => {
+                                let label = `${field}: `;
                                 if (field === '__all__') {
                                     label = '';
                                 }
-                                return (<li>{label}{registrationErrors[field]}</li>);
+                                return (
+                                    <li key={field}>
+                                        {label}{registrationErrors[field]}
+                                    </li>
+                                );
                             })}
                         </ul>
                     </Steps.RegistrationError>
@@ -131,46 +143,62 @@ var StudentCompleteRegistration = intl.injectIntl(React.createClass({
                         <Spinner />
                     ) : (
                         <Progression {... this.state}>
-                            <Steps.ClassInviteExistingStudentStep classroom={this.state.classroom}
-                                                                  onHandleLogOut={this.handleLogOut}
-                                                                  onNextStep={this.advanceStep}
-                                                                  studentUsername={this.props.studentUsername}
-                                                   waiting={this.state.waiting} />
+                            <Steps.ClassInviteExistingStudentStep
+                                classroom={this.state.classroom}
+                                studentUsername={this.props.studentUsername}
+                                waiting={this.state.waiting}
+                                onHandleLogOut={this.handleLogOut}
+                                onNextStep={this.handleAdvanceStep}
+                            />
                             {this.props.must_reset_password ?
-                                <Steps.ChoosePasswordStep onNextStep={this.advanceStep}
-                                                          showPassword={true}
-                                                          waiting={this.state.waiting}
-                                                          username={this.props.studentUsername} />
-                            :
-                                []
+                                <Steps.ChoosePasswordStep
+                                    showPassword
+                                    username={this.props.studentUsername}
+                                    waiting={this.state.waiting}
+                                    onNextStep={this.handleAdvanceStep}
+                                /> : []
                             }
-                            <Steps.DemographicsStep description={demographicsDescription}
-                                                    onNextStep={this.register}
-                                                    waiting={this.state.waiting} />
-                            <Steps.ClassWelcomeStep classroom={this.state.classroom}
-                                                    onNextStep={this.goToClass}
-                                                    waiting={this.state.waiting} />
+                            <Steps.DemographicsStep
+                                description={demographicsDescription}
+                                waiting={this.state.waiting}
+                                onNextStep={this.handleRegister}
+                            />
+                            <Steps.ClassWelcomeStep
+                                classroom={this.state.classroom}
+                                waiting={this.state.waiting}
+                                onNextStep={this.handleGoToClass}
+                            />
                         </Progression>
                     )
                 )}
             </Deck>
         );
     }
-}));
+}
 
-var mapStateToProps = function (state) {
-    return {
-        classroomId: state.session.session.user && state.session.session.user.classroomId,
-        must_reset_password: state.session.session.flags && state.session.session.flags.must_reset_password,
-        newStudent: (
-            state.session.session.permissions &&
-            state.session.session.permissions.student &&
-            state.session.session.flags.must_complete_registration),
-        sessionFetched: state.session.status === sessionStatus.FETCHED,
-        studentUsername: state.session.session.user && state.session.session.user.username
-    };
+StudentCompleteRegistration.propTypes = {
+    classroomId: PropTypes.number.isRequired,
+    intl: intlShape,
+    must_reset_password: PropTypes.bool.isRequired,
+    newStudent: PropTypes.bool.isRequired,
+    sessionFetched: PropTypes.bool.isRequired,
+    studentUsername: PropTypes.string.isRequired
 };
 
-var ConnectedStudentCompleteRegistration = connect(mapStateToProps)(StudentCompleteRegistration);
+const IntlStudentCompleteRegistration = injectIntl(StudentCompleteRegistration);
+
+const mapStateToProps = state => ({
+    classroomId: state.session.session.user && state.session.session.user.classroomId,
+    must_reset_password: state.session.session.flags && state.session.session.flags.must_reset_password,
+    newStudent: (
+        state.session.session.permissions &&
+        state.session.session.permissions.student &&
+        state.session.session.flags.must_complete_registration
+    ),
+    sessionFetched: state.session.status === sessionStatus.FETCHED,
+    studentUsername: state.session.session.user && state.session.session.user.username
+});
+
+const ConnectedStudentCompleteRegistration = connect(mapStateToProps)(IntlStudentCompleteRegistration);
 
 render(<ConnectedStudentCompleteRegistration />, document.getElementById('app'));
diff --git a/src/views/studentregistration/studentregistration.jsx b/src/views/studentregistration/studentregistration.jsx
index e20428dc5..2a548d809 100644
--- a/src/views/studentregistration/studentregistration.jsx
+++ b/src/views/studentregistration/studentregistration.jsx
@@ -1,45 +1,43 @@
-var defaults = require('lodash.defaultsdeep');
-var React = require('react');
-var render = require('../../lib/render.jsx');
+const bindAll = require('lodash.bindall');
+const defaults = require('lodash.defaultsdeep');
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var api = require('../../lib/api');
-var intl = require('../../lib/intl.jsx');
+const api = require('../../lib/api');
+const injectIntl = require('../../lib/intl.jsx').injectIntl;
+const intlShape = require('../../lib/intl.jsx').intlShape;
 
-var Deck = require('../../components/deck/deck.jsx');
-var Progression = require('../../components/progression/progression.jsx');
-var Steps = require('../../components/registration/steps.jsx');
+const Deck = require('../../components/deck/deck.jsx');
+const Progression = require('../../components/progression/progression.jsx');
+const Steps = require('../../components/registration/steps.jsx');
+
+const render = require('../../lib/render.jsx');
 
 require('./studentregistration.scss');
 
-var StudentRegistration = intl.injectIntl(React.createClass({
-    type: 'StudentRegistration',
-    getDefaultProps: function () {
-        return {
-            classroomId: null,
-            classroomToken: null
-        };
-    },
-    getInitialState: function () {
-        return {
+class StudentRegistration extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleRegister',
+            'handleAdvanceStep',
+            'handleGoToClass'
+        ]);
+        this.state = {
             formData: {},
             registrationError: null,
             step: 0,
             waiting: false
         };
-    },
-    advanceStep: function (formData) {
-        formData = formData || {};
-        this.setState({
-            step: this.state.step + 1,
-            formData: defaults({}, formData, this.state.formData)
-        });
-    },
-    componentDidMount: function () {
-        this.setState({waiting: true});
+    }
+    componentDidMount () {
+        this.setState({waiting: true}); // eslint-disable-line react/no-did-mount-set-state
         api({
-            uri: '/classrooms/' + this.props.classroomId,
-            params: {token: this.props.classroomToken}
-        }, function (err, body, res) {
+            uri: `/classrooms/${this.props.classroomId}`,
+            params: {
+                token: this.props.classroomToken
+            }
+        }, (err, body, res) => {
             this.setState({waiting: false});
             if (err) {
                 return this.setState({
@@ -50,13 +48,17 @@ var StudentRegistration = intl.injectIntl(React.createClass({
             }
             if (res.statusCode === 404) {
                 // TODO: Use react-router for this
-                return window.location = '/404';
+                window.location = '/404';
             }
-            this.setState({classroom: body});
-        }.bind(this));
-    },
-    register: function (formData) {
-        this.setState({waiting: true});
+            this.setState({
+                classroom: body
+            });
+        });
+    }
+    handleRegister (formData) {
+        this.setState({
+            waiting: true
+        });
         formData = defaults({}, formData || {}, this.state.formData);
         api({
             host: '',
@@ -68,8 +70,7 @@ var StudentRegistration = intl.injectIntl(React.createClass({
                 password: formData.user.password,
                 birth_month: formData.user.birth.month,
                 birth_year: formData.user.birth.year,
-                gender: (
-                    formData.user.gender === 'other' ?
+                gender: (formData.user.gender === 'other' ?
                     formData.user.genderOther :
                     formData.user.gender
                 ),
@@ -78,66 +79,98 @@ var StudentRegistration = intl.injectIntl(React.createClass({
                 classroom_id: this.props.classroomId,
                 classroom_token: this.props.classroomToken
             }
-        }, function (err, body, res) {
-            this.setState({waiting: false});
+        }, (err, body, res) => {
+            this.setState({
+                waiting: false
+            });
             if (err) return this.setState({registrationError: err});
-            if (body[0] && body[0].success) return this.advanceStep(formData);
+            if (body[0] && body[0].success) return this.handleAdvanceStep(formData);
             this.setState({
                 registrationError:
                     (body[0] && body[0].msg) ||
-                    this.props.intl.formatMessage({id: 'registration.generalError'}) + ' (' + res.statusCode + ')'
+                    `${this.props.intl.formatMessage({id: 'registration.generalError'})} (${res.statusCode})`
             });
-        }.bind(this));
-    },
-    goToClass: function () {
-        window.location = '/classes/' + this.props.classroomId + '/';
-    },
-    render: function () {
-        var demographicsDescription = this.props.intl.formatMessage({
-            id: 'registration.studentPersonalStepDescription'});
-        var usernameTitle = this.props.intl.formatMessage({id: 'registration.usernameStepTitleScratcher'});
-        var usernameHelp = this.props.intl.formatMessage({id: 'registration.studentUsernameFieldHelpText'});
-        var usernameDescription = (
-            this.props.intl.formatMessage({id: 'registration.studentUsernameStepDescription'}) + ' ' +
-            this.props.intl.formatMessage({id: 'registration.studentUsernameStepHelpText'})
-        );
-        var usernameTooltip = this.props.intl.formatMessage({id: 'registration.studentUsernameStepTooltip'});
+        });
+    }
+    handleAdvanceStep (formData) {
+        formData = formData || {};
+        this.setState({
+            step: this.state.step + 1,
+            formData: defaults({}, formData, this.state.formData)
+        });
+    }
+    handleGoToClass () {
+        window.location = `/classes/${this.props.classroomId}/`;
+    }
+    render () {
+        const usernameDescription = this.props.intl.formatMessage({id: 'registration.studentUsernameStepDescription'});
+        const usernameHelp = this.props.intl.formatMessage({id: 'registration.studentUsernameStepHelpText'});
         return (
             <Deck className="student-registration">
                 {this.state.registrationError ?
                     <Steps.RegistrationError>
                         {this.state.registrationError}
-                    </Steps.RegistrationError>
-                :
-                    <Progression {... this.state}>
-                        <Steps.ClassInviteNewStudentStep classroom={this.state.classroom}
-                                                         onNextStep={this.advanceStep}
-                                                         waiting={this.state.waiting || !this.state.classroom} />
-                        <Steps.UsernameStep onNextStep={this.advanceStep}
-                                            title={usernameTitle}
-                                            description={usernameDescription}
-                                            tooltip={usernameTooltip}
-                                            usernameHelp={usernameHelp}
-                                            waiting={this.state.waiting} />
-                        <Steps.DemographicsStep description={demographicsDescription}
-                                                onNextStep={this.register}
-                                                waiting={this.state.waiting} />
-                        <Steps.ClassWelcomeStep classroom={this.state.classroom}
-                                                onNextStep={this.goToClass}
-                                                waiting={this.state.waiting || !this.state.classroom} />
+                    </Steps.RegistrationError> :
+                    <Progression {...this.state}>
+                        <Steps.ClassInviteNewStudentStep
+                            classroom={this.state.classroom}
+                            waiting={this.state.waiting || !this.state.classroom}
+                            onNextStep={this.handleAdvanceStep}
+                        />
+                        <Steps.UsernameStep
+                            description={`${usernameDescription} ${usernameHelp}`}
+                            title={this.props.intl.formatMessage({
+                                id: 'registration.usernameStepTitleScratcher'
+                            })}
+                            tooltip={this.props.intl.formatMessage({
+                                id: 'registration.studentUsernameStepTooltip'
+                            })}
+                            usernameHelp={this.props.intl.formatMessage({
+                                id: 'registration.studentUsernameFieldHelpText'
+                            })}
+                            waiting={this.state.waiting}
+                            onNextStep={this.handleAdvanceStep}
+                        />
+                        <Steps.DemographicsStep
+                            description={this.props.intl.formatMessage({
+                                id: 'registration.studentPersonalStepDescription'
+                            })}
+                            waiting={this.state.waiting}
+                            onNextStep={this.handleRegister}
+                        />
+                        <Steps.ClassWelcomeStep
+                            classroom={this.state.classroom}
+                            waiting={this.state.waiting || !this.state.classroom}
+                            onNextStep={this.handleGoToClass}
+                        />
                     </Progression>
                 }
             </Deck>
         );
     }
-}));
+}
 
-var [classroomId, _, classroomToken] = document.location.pathname.split('/')
-    .filter(function (p) {
-        if (p) return p;
-    })
+StudentRegistration.propTypes = {
+    classroomId: PropTypes.number.isRequired,
+    classroomToken: PropTypes.string.isRequired,
+    intl: intlShape
+};
+
+StudentRegistration.defaultProps = {
+    classroomId: null,
+    classroomToken: null
+};
+
+const IntlStudentRegistration = injectIntl(StudentRegistration);
+
+const [classroomId, _, classroomToken] = document.location.pathname.split('/').filter(p => {
+    if (p) {
+        return p;
+    }
+    return null;
+})
     .slice(-3);
 
-var props = {classroomId, classroomToken};
+const props = {classroomId, classroomToken};
 
-render(<StudentRegistration {... props} />, document.getElementById('app'));
+render(<IntlStudentRegistration {...props} />, document.getElementById('app'));
diff --git a/src/views/teacherregistration/teacherregistration.jsx b/src/views/teacherregistration/teacherregistration.jsx
index 23fa0a3b3..6f9eab8bf 100644
--- a/src/views/teacherregistration/teacherregistration.jsx
+++ b/src/views/teacherregistration/teacherregistration.jsx
@@ -1,37 +1,45 @@
-var connect = require('react-redux').connect;
-var defaults = require('lodash.defaultsdeep');
-var React = require('react');
-var render = require('../../lib/render.jsx');
+const bindAll = require('lodash.bindall');
+const connect = require('react-redux').connect;
+const defaults = require('lodash.defaultsdeep');
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var api = require('../../lib/api');
-var intl = require('../../lib/intl.jsx');
-var sessionActions = require('../../redux/session.js');
+const api = require('../../lib/api');
+const injectIntl = require('../../lib/intl.jsx').injectIntl;
+const intlShape = require('../../lib/intl.jsx').intlShape;
+const sessionActions = require('../../redux/session.js');
 
-var Deck = require('../../components/deck/deck.jsx');
-var Progression = require('../../components/progression/progression.jsx');
-var Steps = require('../../components/registration/steps.jsx');
+const Deck = require('../../components/deck/deck.jsx');
+const Progression = require('../../components/progression/progression.jsx');
+const Steps = require('../../components/registration/steps.jsx');
+
+const render = require('../../lib/render.jsx');
 
 require('./teacherregistration.scss');
 
 
-var TeacherRegistration = intl.injectIntl(React.createClass({
-    type: 'TeacherRegistration',
-    getInitialState: function () {
-        return {
+class TeacherRegistration extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleAdvanceStep',
+            'handleRegister'
+        ]);
+        this.state = {
             formData: {},
             registrationError: null,
-            step: 0,
+            step: 6,
             waiting: false
         };
-    },
-    advanceStep: function (formData) {
+    }
+    handleAdvanceStep (formData) {
         formData = formData || {};
         this.setState({
             step: this.state.step + 1,
             formData: defaults({}, formData, this.state.formData)
         });
-    },
-    register: function (formData) {
+    }
+    handleRegister (formData) {
         this.setState({waiting: true});
         api({
             host: '',
@@ -46,8 +54,8 @@ var TeacherRegistration = intl.injectIntl(React.createClass({
                 birth_year: this.state.formData.user.birth.year,
                 gender: (
                     this.state.formData.user.gender === 'other' ?
-                    this.state.formData.user.genderOther :
-                    this.state.formData.user.gender
+                        this.state.formData.user.genderOther :
+                        this.state.formData.user.gender
                 ),
                 country: this.state.formData.user.country,
                 subscribe: formData.subscribe,
@@ -68,70 +76,107 @@ var TeacherRegistration = intl.injectIntl(React.createClass({
                 address_zip: this.state.formData.address.zip,
                 how_use_scratch: this.state.formData.useScratch
             }
-        }, function (err, body, res) {
+        }, (err, body, res) => {
             this.setState({waiting: false});
             if (err) return this.setState({registrationError: err});
             if (body[0] && body[0].success) {
                 this.props.dispatch(sessionActions.refreshSession());
-                return this.advanceStep(formData);
+                return this.handleAdvanceStep(formData);
             }
             this.setState({
                 registrationError:
                     (body[0] && body[0].msg) ||
-                    this.props.intl.formatMessage({id: 'registration.generalError'}) + ' (' + res.statusCode + ')'
+                    `${this.props.intl.formatMessage({id: 'registration.generalError'})} (${res.statusCode})`
             });
-        }.bind(this));
-    },
-    render: function () {
-        var permissions = this.props.session.permissions || {};
+        });
+    }
+    render () {
+        const permissions = this.props.session.permissions || {};
+        
         return (
             <Deck className="teacher-registration">
                 {this.state.registrationError ?
                     <Steps.RegistrationError>
                         {this.state.registrationError}
-                    </Steps.RegistrationError>
-                :
-                    <Progression {... this.state}>
-                        <Steps.UsernameStep onNextStep={this.advanceStep}
-                                            waiting={this.state.waiting} />
-                        <Steps.DemographicsStep onNextStep={this.advanceStep}
-                                                waiting={this.state.waiting}
-                                                birthOffset={13} />
-                        <Steps.NameStep onNextStep={this.advanceStep}
-                                        waiting={this.state.waiting} />
-                        <Steps.PhoneNumberStep onNextStep={this.advanceStep}
-                                               waiting={this.state.waiting}
-                                               defaultCountry={
-                                                   this.state.formData.user && this.state.formData.user.country
-                                               } />
-                        <Steps.OrganizationStep onNextStep={this.advanceStep}
-                                                waiting={this.state.waiting} />
-                        <Steps.AddressStep onNextStep={this.advanceStep}
-                                           waiting={this.state.waiting}
-                                           defaultCountry={
-                                               this.state.formData.user && this.state.formData.user.country
-                                           } />
-                        <Steps.UseScratchStep onNextStep={this.advanceStep}
-                                              waiting={this.state.waiting} />
-                        <Steps.EmailStep onNextStep={this.register}
-                                         waiting={this.state.waiting} />
-                        <Steps.TeacherApprovalStep email={this.state.formData.user && this.state.formData.user.email}
-                                                   confirmed={permissions.social}
-                                                   invited={permissions.educator_invitee}
-                                                   educator={permissions.educator} />
+                    </Steps.RegistrationError> :
+                    <Progression step={this.state.step}>
+                        <Steps.UsernameStep
+                            waiting={this.state.waiting}
+                            onNextStep={this.handleAdvanceStep}
+                        />
+                        <Steps.DemographicsStep
+                            birthOffset={13}
+                            waiting={this.state.waiting}
+                            onNextStep={this.handleAdvanceStep}
+                        />
+                        <Steps.NameStep
+                            waiting={this.state.waiting}
+                            onNextStep={this.handleAdvanceStep}
+                        />
+                        <Steps.PhoneNumberStep
+                            defaultCountry={
+                                this.state.formData.user && this.state.formData.user.country
+                            }
+                            waiting={this.state.waiting}
+                            onNextStep={this.handleAdvanceStep}
+                        />
+                        <Steps.OrganizationStep
+                            waiting={this.state.waiting}
+                            onNextStep={this.handleAdvanceStep}
+                        />
+                        <Steps.AddressStep
+                            defaultCountry={
+                                this.state.formData.user && this.state.formData.user.country
+                            }
+                            waiting={this.state.waiting}
+                            onNextStep={this.handleAdvanceStep}
+                        />
+                        <Steps.UseScratchStep
+                            waiting={this.state.waiting}
+                            onNextStep={this.handleAdvanceStep}
+                        />
+                        <Steps.EmailStep
+                            waiting={this.state.waiting}
+                            onNextStep={this.handleRegister}
+                        />
+                        <Steps.TeacherApprovalStep
+                            confirmed={permissions.social}
+                            educator={permissions.educator}
+                            email={this.state.formData.user && this.state.formData.user.email}
+                            invited={permissions.educator_invitee}
+                        />
                     </Progression>
                 }
             </Deck>
         );
     }
-}));
+}
 
-var mapStateToProps = function (state) {
-    return {
-        session: state.session.session
-    };
+TeacherRegistration.propTypes = {
+    dispatch: PropTypes.func,
+    intl: intlShape,
+    session: PropTypes.shape({
+        user: PropTypes.shape({
+            classroomId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
+            thumbnailUrl: PropTypes.string,
+            username: PropTypes.string
+        }),
+        permissions: PropTypes.shape({
+            admin: PropTypes.bool,
+            social: PropTypes.bool,
+            educator: PropTypes.bool,
+            educator_invitee: PropTypes.bool,
+            student: PropTypes.bool
+        })
+    })
 };
 
-var ConnectedTeacherRegistration = connect(mapStateToProps)(TeacherRegistration);
+const IntlTeacherRegistration = injectIntl(TeacherRegistration);
+
+const mapStateToProps = state => ({
+    session: state.session.session
+});
+
+const ConnectedTeacherRegistration = connect(mapStateToProps)(IntlTeacherRegistration);
 
 render(<ConnectedTeacherRegistration />, document.getElementById('app'));
diff --git a/src/views/teachers/faq/faq.jsx b/src/views/teachers/faq/faq.jsx
index 828a7fbbc..17dcc56e9 100644
--- a/src/views/teachers/faq/faq.jsx
+++ b/src/views/teachers/faq/faq.jsx
@@ -1,104 +1,113 @@
-var FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
-var FormattedMessage = require('react-intl').FormattedMessage;
-var injectIntl = require('react-intl').injectIntl;
-var React = require('react');
-var render = require('../../../lib/render.jsx');
+const FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
+const FormattedMessage = require('react-intl').FormattedMessage;
+const injectIntl = require('react-intl').injectIntl;
+const intlShape = require('react-intl').intlShape;
+const React = require('react');
 
-var Page = require('../../../components/page/www/page.jsx');
-var InformationPage = require('../../../components/informationpage/informationpage.jsx');
+const InformationPage = require('../../../components/informationpage/informationpage.jsx');
 
-var TeacherFaq = injectIntl(React.createClass({
-    type: 'TeacherFaq',
-    render: function () {
-        var formatMessage = this.props.intl.formatMessage;
-        return (
-            <InformationPage title={formatMessage({id: 'teacherfaq.title'})}>
-                <div className="inner info-inner">
-                    <section id="teacher-accounts">
-                        <span className="nav-spacer"></span>
-                        <h2><FormattedMessage id='teacherfaq.title' /></h2>
-                        <dl>
-                            <dt><FormattedMessage id='teacherfaq.teacherWhatTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='teacherfaq.teacherWhatBody' /></dd>
-                            <iframe width="565" height="318" src="https://www.youtube.com/embed/7Hl9GxA1zwQ"
-                            frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
-                            <dt><FormattedMessage id='teacherfaq.teacherSignUpTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='teacherfaq.teacherSignUpBody' /></dd>
-                            <dt><FormattedMessage id='teacherfaq.teacherWaitTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='teacherfaq.teacherWaitBody' /></dd>
-                            <dt><FormattedMessage id='teacherfaq.teacherPersonalTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='teacherfaq.teacherPersonalBody' /></dd>
-                            <dt><FormattedMessage id='teacherfaq.teacherGoogleTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='teacherfaq.teacherGoogleBody' /></dd>
-                            <dt><FormattedMessage id='teacherfaq.teacherEdTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='teacherfaq.teacherEdBody' /></dd>
-                            <dt><FormattedMessage id='teacherfaq.teacherMultipleTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='teacherfaq.teacherMultipleBody' /></dd>
-                            <dt><FormattedMessage id='teacherfaq.teacherQuestionsTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='teacherfaq.teacherQuestionsBody' /></dd>
-                        </dl>
-                    </section>
-                    <section id="student-accounts">
-                        <span className="nav-spacer"></span>
-                        <h2><FormattedMessage id='teacherfaq.studentAccountsTitle' /></h2>
-                        <dl>
-                            <dt><FormattedMessage id='teacherfaq.studentVerifyTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='teacherfaq.studentVerifyBody' /></dd>
-                            <dt><FormattedMessage id='teacherfaq.studentEndTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='teacherfaq.studentEndBody' /></dd>
-                            <dt><FormattedMessage id='teacherfaq.studentForgetTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='teacherfaq.studentForgetBody' /></dd>
-                            <dt><FormattedMessage id='teacherfaq.studentUnsharedTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='teacherfaq.studentUnsharedBody' /></dd>
-                            <dt><FormattedMessage id='teacherfaq.studentDeleteTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='teacherfaq.studentDeleteBody' /></dd>
-                            <dt><FormattedMessage id='teacherfaq.studentAddTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='teacherfaq.studentAddBody' /></dd>
-                            <dt><FormattedMessage id='teacherfaq.studentMultipleTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='teacherfaq.studentMultipleBody' /></dd>
-                            <dt><FormattedMessage id='teacherfaq.studentDiscussTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='teacherfaq.studentDiscussBody' /></dd>
-                            <dt><FormattedMessage id='teacherfaq.studentDataTitle' /></dt>
-                            <dd><FormattedMessage id='teacherfaq.studentDataBody' /></dd>
-                            <dt><FormattedMessage id='teacherfaq.studentPrivacyLawsTitle' /></dt>
-                            <dd><FormattedMessage id='teacherfaq.studentPrivacyLawsBody' /></dd>
-                        </dl>
-                    </section>
-                    <section id="community">
-                        <span className="nav-spacer"></span>
-                        <h2><FormattedMessage id='teacherfaq.commTitle' /></h2>
-                        <dl>
-                            <dt><FormattedMessage id='teacherfaq.commHiddenTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='teacherfaq.commHiddenBody' /></dd>
-                            <dt><FormattedMessage id='teacherfaq.commWhoTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='teacherfaq.commWhoBody' /></dd>
-                            <dt><FormattedMessage id='teacherfaq.commInappropriateTitle' /></dt>
-                            <dd><FormattedHTMLMessage id='teacherfaq.commInappropriateBody' /></dd>
-                        </dl>
-                    </section>
-                </div>
-                <nav>
-                    <ol>
-                        <li>
-                            <a href="#teacher-accounts">
-                                <FormattedMessage id='teacherfaq.title' />
-                            </a>
-                        </li>
-                        <li>
-                            <a href="#student-accounts">
-                                <FormattedMessage id='teacherfaq.studentAccountsTitle' />
-                            </a>
-                        </li>
-                        <li>
-                            <a href="#community">
-                                <FormattedMessage id='teacherfaq.commTitle' />
-                            </a>
-                        </li>
-                    </ol>
-                </nav>
-            </InformationPage>
-        );
-    }
-}));
+const Page = require('../../../components/page/www/page.jsx');
+const render = require('../../../lib/render.jsx');
 
-render(<Page><TeacherFaq /></Page>, document.getElementById('app'));
+const TeacherFaq = props => (
+    <InformationPage title={props.intl.formatMessage({id: 'teacherfaq.title'})}>
+        <div className="inner info-inner">
+            <section id="teacher-accounts">
+                <span className="nav-spacer" />
+                <h2><FormattedMessage id="teacherfaq.title" /></h2>
+                <dl>
+                    <dt><FormattedMessage id="teacherfaq.teacherWhatTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="teacherfaq.teacherWhatBody" /></dd>
+                    <iframe
+                        allowFullscreen
+                        mozallowfullscreen
+                        webkitallowfullscreen
+                        frameBorder="0"
+                        height="318"
+                        src="https://www.youtube.com/embed/7Hl9GxA1zwQ"
+                        width="565"
+                    />
+                    <dt><FormattedMessage id="teacherfaq.teacherSignUpTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="teacherfaq.teacherSignUpBody" /></dd>
+                    <dt><FormattedMessage id="teacherfaq.teacherWaitTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="teacherfaq.teacherWaitBody" /></dd>
+                    <dt><FormattedMessage id="teacherfaq.teacherPersonalTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="teacherfaq.teacherPersonalBody" /></dd>
+                    <dt><FormattedMessage id="teacherfaq.teacherGoogleTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="teacherfaq.teacherGoogleBody" /></dd>
+                    <dt><FormattedMessage id="teacherfaq.teacherEdTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="teacherfaq.teacherEdBody" /></dd>
+                    <dt><FormattedMessage id="teacherfaq.teacherMultipleTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="teacherfaq.teacherMultipleBody" /></dd>
+                    <dt><FormattedMessage id="teacherfaq.teacherQuestionsTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="teacherfaq.teacherQuestionsBody" /></dd>
+                </dl>
+            </section>
+            <section id="student-accounts">
+                <span className="nav-spacer" />
+                <h2><FormattedMessage id="teacherfaq.studentAccountsTitle" /></h2>
+                <dl>
+                    <dt><FormattedMessage id="teacherfaq.studentVerifyTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="teacherfaq.studentVerifyBody" /></dd>
+                    <dt><FormattedMessage id="teacherfaq.studentEndTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="teacherfaq.studentEndBody" /></dd>
+                    <dt><FormattedMessage id="teacherfaq.studentForgetTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="teacherfaq.studentForgetBody" /></dd>
+                    <dt><FormattedMessage id="teacherfaq.studentUnsharedTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="teacherfaq.studentUnsharedBody" /></dd>
+                    <dt><FormattedMessage id="teacherfaq.studentDeleteTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="teacherfaq.studentDeleteBody" /></dd>
+                    <dt><FormattedMessage id="teacherfaq.studentAddTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="teacherfaq.studentAddBody" /></dd>
+                    <dt><FormattedMessage id="teacherfaq.studentMultipleTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="teacherfaq.studentMultipleBody" /></dd>
+                    <dt><FormattedMessage id="teacherfaq.studentDiscussTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="teacherfaq.studentDiscussBody" /></dd>
+                    <dt><FormattedMessage id="teacherfaq.studentDataTitle" /></dt>
+                    <dd><FormattedMessage id="teacherfaq.studentDataBody" /></dd>
+                    <dt><FormattedMessage id="teacherfaq.studentPrivacyLawsTitle" /></dt>
+                    <dd><FormattedMessage id="teacherfaq.studentPrivacyLawsBody" /></dd>
+                </dl>
+            </section>
+            <section id="community">
+                <span className="nav-spacer" />
+                <h2><FormattedMessage id="teacherfaq.commTitle" /></h2>
+                <dl>
+                    <dt><FormattedMessage id="teacherfaq.commHiddenTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="teacherfaq.commHiddenBody" /></dd>
+                    <dt><FormattedMessage id="teacherfaq.commWhoTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="teacherfaq.commWhoBody" /></dd>
+                    <dt><FormattedMessage id="teacherfaq.commInappropriateTitle" /></dt>
+                    <dd><FormattedHTMLMessage id="teacherfaq.commInappropriateBody" /></dd>
+                </dl>
+            </section>
+        </div>
+        <nav>
+            <ol>
+                <li>
+                    <a href="#teacher-accounts">
+                        <FormattedMessage id="teacherfaq.title" />
+                    </a>
+                </li>
+                <li>
+                    <a href="#student-accounts">
+                        <FormattedMessage id="teacherfaq.studentAccountsTitle" />
+                    </a>
+                </li>
+                <li>
+                    <a href="#community">
+                        <FormattedMessage id="teacherfaq.commTitle" />
+                    </a>
+                </li>
+            </ol>
+        </nav>
+    </InformationPage>
+);
+
+TeacherFaq.propTypes = {
+    intl: intlShape
+};
+
+const IntlTeacherFaq = injectIntl(TeacherFaq);
+
+render(<Page><IntlTeacherFaq /></Page>, document.getElementById('app'));
diff --git a/src/views/teachers/landing/landing.jsx b/src/views/teachers/landing/landing.jsx
index 2b36903bb..b398e2823 100644
--- a/src/views/teachers/landing/landing.jsx
+++ b/src/views/teachers/landing/landing.jsx
@@ -1,134 +1,159 @@
-var React = require('react');
-var render = require('../../../lib/render.jsx');
+const FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
+const FormattedMessage = require('react-intl').FormattedMessage;
+const React = require('react');
 
-var FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
-var FormattedMessage = require('react-intl').FormattedMessage;
-var injectIntl = require('react-intl').injectIntl;
+const FlexRow = require('../../../components/flex-row/flex-row.jsx');
+const SubNavigation = require('../../../components/subnavigation/subnavigation.jsx');
+const TitleBanner = require('../../../components/title-banner/title-banner.jsx');
 
-var Page = require('../../../components/page/www/page.jsx');
-var FlexRow = require('../../../components/flex-row/flex-row.jsx');
-var SubNavigation = require('../../../components/subnavigation/subnavigation.jsx');
-var TitleBanner = require('../../../components/title-banner/title-banner.jsx');
+const Page = require('../../../components/page/www/page.jsx');
+const render = require('../../../lib/render.jsx');
 
 require('./landing.scss');
 
-var Landing = injectIntl(React.createClass({
-    type: 'Landing',
-    render: function () {
-        return (
-            <div className="educators">
-                <TitleBanner className="masthead">
-                    <div className="inner">
-                        <h1 className="title-banner-h1">
-                            <FormattedMessage id="teacherlanding.title" />
-                        </h1>
-                        <FlexRow className="masthead-info">
-                            <p className="title-banner-p intro">
-                                <FormattedMessage id="teacherlanding.intro" />
-                            </p>
-                            <div className="ted-talk">
-                                <iframe src="https://www.youtube.com/embed/uPSuG063jhA?border=0&wmode=transparent"
-                                    frameBorder="0" allowFullScreen></iframe>
-                            </div>
-                        </FlexRow>
+const Landing = () => (
+    <div className="educators">
+        <TitleBanner className="masthead">
+            <div className="inner">
+                <h1 className="title-banner-h1">
+                    <FormattedMessage id="teacherlanding.title" />
+                </h1>
+                <FlexRow className="masthead-info">
+                    <p className="title-banner-p intro">
+                        <FormattedMessage id="teacherlanding.intro" />
+                    </p>
+                    <div className="ted-talk">
+                        <iframe
+                            allowFullScreen
+                            frameBorder="0"
+                            src="https://www.youtube.com/embed/uPSuG063jhA?border=0&wmode=transparent"
+                        />
                     </div>
-                    <div className="band">
-                        <SubNavigation className="inner">
-                            <a href="#in-practice">
-                                <li>
-                                    <FormattedMessage id="teacherlanding.inPracticeTitle" />
-                                </li>
-                            </a>
-                            <a href="#resources">
-                                <li>
-                                    <FormattedMessage id="teacherlanding.resourcesAnchor" />
-                                </li>
-                            </a>
-                            <a href="#teacher-accounts">
-                                <li>
-                                    <FormattedMessage id="general.teacherAccounts" />
-                                </li>
-                            </a>
-                        </SubNavigation>
-                    </div>
-                </TitleBanner>
-
-                <div className="inner">
-                    <section id="in-practice">
-                        <span className="nav-spacer"></span>
-                        <h2><FormattedMessage id="teacherlanding.inPracticeTitle" /></h2>
-                        <p className="intro"><FormattedMessage id="teacherlanding.inPracticeIntro" /></p>
-                        <FlexRow className="general-usage">
-                            <p><FormattedHTMLMessage id="teacherlanding.generalUsageSettings" /></p>
-                            <p><FormattedHTMLMessage id="teacherlanding.generalUsageGradeLevels" /></p>
-                            <p><FormattedHTMLMessage id="teacherlanding.generalUsageSubjectAreas"/></p>
-                        </FlexRow>
-                    </section>
-                    <section id="resources">
-                        <span className="nav-spacer"></span>
-                        <h2><FormattedMessage id="general.resourcesTitle" /></h2>
-                        <FlexRow className="educator-community">
-                            <div>
-                                <h3><FormattedMessage id="teacherlanding.scratchEdTitle" /></h3>
-                                <p>
-                                    <FormattedHTMLMessage id="teacherlanding.scratchEdDescription" />
-                                </p>
-                            </div>
-                            <div>
-                                <h3><FormattedMessage id="teacherlanding.meetupTitle" /></h3>
-                                <p>
-                                    <FormattedHTMLMessage id="teacherlanding.meetupDescription" />
-                                </p>
-                            </div>
-                        </FlexRow>
-                        <h3 id="guides-header"><FormattedMessage id="teacherlanding.guidesTitle" /></h3>
-                        <FlexRow className="guides-and-tutorials">
-                            <div>
-                                <a href="/tips">
-                                    <img src="/svgs/teachers/v2-cards.svg" alt="cards icon" />
-                                </a>
-                                <p>
-                                    <FormattedHTMLMessage id="teacherlanding.tttPage" />
-                                </p>
-                            </div>
-                            <div>
-                                <a href="/projects/editor/?tip_bar=home">
-                                    <img src="/svgs/teachers/tips-window.svg" alt="tips window icon" />
-                                </a>
-                                <p>
-                                    <FormattedHTMLMessage id="teacherlanding.tipsWindow" />
-                                </p>
-                            </div>
-                            <div>
-                                <a href="http://scratched.gse.harvard.edu/guide/">
-                                    <img src="/svgs/teachers/creative-computing.svg" alt="creative computing icon" />
-                                </a>
-                                <p>
-                                    <FormattedHTMLMessage id="teacherlanding.creativeComputing" />
-                                </p>
-                            </div>
-                        </FlexRow>
-                    </section>
-                </div>
-                <div id="teacher-accounts">
-                    <div className="inner account-flex">
-                        <div id="left">
-                            <h2><FormattedMessage id="teacherlanding.accountsTitle" /></h2>
-                            <p>
-                                <FormattedHTMLMessage id="teacherlanding.accountsDescription" />
-                            </p>
-                            <SubNavigation className="teacher-account-buttons" align="left">
-                                <a href="/educators/register">
-                                    <li><FormattedMessage id="teacherlanding.requestAccount" /></li>
-                                </a>
-                            </SubNavigation>
-                        </div>
-                        <img src="/images/teachers/teacher-account.png" alt="teacher account" id="teacher-icon"/>
-                    </div>
-                </div>
+                </FlexRow>
             </div>
-        );
-    }
-}));
+            <div className="band">
+                <SubNavigation className="inner">
+                    <a href="#in-practice">
+                        <li>
+                            <FormattedMessage id="teacherlanding.inPracticeTitle" />
+                        </li>
+                    </a>
+                    <a href="#resources">
+                        <li>
+                            <FormattedMessage id="teacherlanding.resourcesAnchor" />
+                        </li>
+                    </a>
+                    <a href="#teacher-accounts">
+                        <li>
+                            <FormattedMessage id="general.teacherAccounts" />
+                        </li>
+                    </a>
+                </SubNavigation>
+            </div>
+        </TitleBanner>
+
+        <div className="inner">
+            <section id="in-practice">
+                <span className="nav-spacer" />
+                <h2>
+                    <FormattedMessage id="teacherlanding.inPracticeTitle" />
+                </h2>
+                <p className="intro">
+                    <FormattedMessage id="teacherlanding.inPracticeIntro" />
+                </p>
+                <FlexRow className="general-usage">
+                    <p><FormattedHTMLMessage id="teacherlanding.generalUsageSettings" /></p>
+                    <p><FormattedHTMLMessage id="teacherlanding.generalUsageGradeLevels" /></p>
+                    <p><FormattedHTMLMessage id="teacherlanding.generalUsageSubjectAreas" /></p>
+                </FlexRow>
+            </section>
+            <section id="resources">
+                <span className="nav-spacer" />
+                <h2><FormattedMessage id="general.resourcesTitle" /></h2>
+                <FlexRow className="educator-community">
+                    <div>
+                        <h3>
+                            <FormattedMessage id="teacherlanding.scratchEdTitle" />
+                        </h3>
+                        <p>
+                            <FormattedHTMLMessage id="teacherlanding.scratchEdDescription" />
+                        </p>
+                    </div>
+                    <div>
+                        <h3>
+                            <FormattedMessage id="teacherlanding.meetupTitle" />
+                        </h3>
+                        <p>
+                            <FormattedHTMLMessage id="teacherlanding.meetupDescription" />
+                        </p>
+                    </div>
+                </FlexRow>
+                <h3 id="guides-header">
+                    <FormattedMessage id="teacherlanding.guidesTitle" />
+                </h3>
+                <FlexRow className="guides-and-tutorials">
+                    <div>
+                        <a href="/tips">
+                            <img
+                                alt="cards icon"
+                                src="/svgs/teachers/v2-cards.svg"
+                            />
+                        </a>
+                        <p>
+                            <FormattedHTMLMessage id="teacherlanding.tttPage" />
+                        </p>
+                    </div>
+                    <div>
+                        <a href="/projects/editor/?tip_bar=home">
+                            <img
+                                alt="tips window icon"
+                                src="/svgs/teachers/tips-window.svg"
+                            />
+                        </a>
+                        <p>
+                            <FormattedHTMLMessage id="teacherlanding.tipsWindow" />
+                        </p>
+                    </div>
+                    <div>
+                        <a href="http://scratched.gse.harconstd.edu/guide/">
+                            <img
+                                alt="creative computing icon"
+                                src="/svgs/teachers/creative-computing.svg"
+                            />
+                        </a>
+                        <p>
+                            <FormattedHTMLMessage id="teacherlanding.creativeComputing" />
+                        </p>
+                    </div>
+                </FlexRow>
+            </section>
+        </div>
+        <div id="teacher-accounts">
+            <div className="inner account-flex">
+                <div id="left">
+                    <h2>
+                        <FormattedMessage id="teacherlanding.accountsTitle" />
+                    </h2>
+                    <p>
+                        <FormattedHTMLMessage id="teacherlanding.accountsDescription" />
+                    </p>
+                    <SubNavigation
+                        align="left"
+                        className="teacher-account-buttons"
+                    >
+                        <a href="/educators/register">
+                            <li><FormattedMessage id="teacherlanding.requestAccount" /></li>
+                        </a>
+                    </SubNavigation>
+                </div>
+                <img
+                    alt="teacher account"
+                    id="teacher-icon"
+                    src="/images/teachers/teacher-account.png"
+                />
+            </div>
+        </div>
+    </div>
+);
 
 render(<Page><Landing /></Page>, document.getElementById('app'));
diff --git a/src/views/teacherwaitingroom/teacherwaitingroom.jsx b/src/views/teacherwaitingroom/teacherwaitingroom.jsx
index 94c0660b3..0c50dea8e 100644
--- a/src/views/teacherwaitingroom/teacherwaitingroom.jsx
+++ b/src/views/teacherwaitingroom/teacherwaitingroom.jsx
@@ -1,32 +1,38 @@
-var classNames = require('classnames');
-var connect = require('react-redux').connect;
-var React = require('react');
-var render = require('../../lib/render.jsx');
+const classNames = require('classnames');
+const connect = require('react-redux').connect;
+const PropTypes = require('prop-types');
+const React = require('react');
 
-var Deck = require ('../../components/deck/deck.jsx');
-var TeacherApprovalStep = require('../../components/registration/steps.jsx').TeacherApprovalStep;
+const Deck = require('../../components/deck/deck.jsx');
+const TeacherApprovalStep = require('../../components/registration/steps.jsx').TeacherApprovalStep;
+
+const render = require('../../lib/render.jsx');
 
 require('./teacherwaitingroom.scss');
 
-var TeacherWaitingRoom = React.createClass({
-    displayName: 'TeacherWaitingRoom',
-    componentWillReceiveProps: function (nextProps) {
+class TeacherWaitingRoom extends React.Component {
+    componentWillReceiveProps (nextProps) {
         if (nextProps.approved) {
             window.location.href = '/educators/classes/';
         }
-    },
-    render: function () {
+    }
+    render () {
         return (
             <Deck className={classNames('teacher-waitingroom', this.props.className)}>
-                <TeacherApprovalStep {... this.props} />
+                <TeacherApprovalStep {...this.props} />
             </Deck>
         );
     }
-});
+}
 
-var mapStateToProps = function (state) {
-    var permissions = state.session.session.permissions || {};
-    var user = state.session.session.user || {};
+TeacherWaitingRoom.propTypes = {
+    approved: PropTypes.bool,
+    className: PropTypes.string
+};
+
+const mapStateToProps = state => {
+    const permissions = state.session.session.permissions || {};
+    const user = state.session.session.user || {};
     return {
         approved: permissions && permissions.educator && !permissions.educator_invitee && permissions.social,
         confirmed: permissions && permissions.social,
@@ -36,6 +42,6 @@ var mapStateToProps = function (state) {
     };
 };
 
-var ConnectedTeacherWaitingRoom = connect(mapStateToProps)(TeacherWaitingRoom);
+const ConnectedTeacherWaitingRoom = connect(mapStateToProps)(TeacherWaitingRoom);
 
 render(<ConnectedTeacherWaitingRoom />, document.getElementById('app'));
diff --git a/src/views/terms/terms.jsx b/src/views/terms/terms.jsx
index 9bc31ccfa..f5f914b99 100644
--- a/src/views/terms/terms.jsx
+++ b/src/views/terms/terms.jsx
@@ -1,575 +1,571 @@
-var React = require('react');
-var render = require('../../lib/render.jsx');
+const React = require('react');
 
-var Page = require('../../components/page/www/page.jsx');
-var InformationPage = require('../../components/informationpage/informationpage.jsx');
+const InformationPage = require('../../components/informationpage/informationpage.jsx');
 
-var Terms = React.createClass({
-    type: 'Terms',
-    render: function () {
-        return (
-            <InformationPage title={'Scratch Terms of Use'}>
-                <div className="inner info-inner">
-                    <section id="user-agreement">
-                        <span className="nav-spacer"></span>
-                        <h3>1. User Agreement</h3>
-                        <p>
-                            1.1 These Terms of Use constitute an agreement between
-                             you and the Scratch Team that governs your use of{' '}
-                             <a href="https://scratch.mit.edu">scratch.mit.edu</a>{' '}
-                             and all associated services, including but not limited
-                             to the <a href="http://day.scratch.mit.edu">Scratch Day</a>{' '}
-                             and <a href="http://scratchx.org">ScratchX</a> websites
-                             (collectively "Scratch"). The Scratch Team is part of the
-                             Lifelong Kindergarten Group in the Media Laboratory at the
-                             Massachusetts Institute of Technology ("MIT"). Please
-                             read the Terms of Use carefully. By using Scratch you
-                             affirm that you have read, understood, and accepted
-                             the terms and conditions in the Terms of Use. If you
-                             do not agree with any of these conditions, please do not
-                             use Scratch.
-                        </p>
-                        <p>
-                            1.2 Your privacy is important to us. Please read our{' '}
-                             <a href="/privacy_policy">Privacy Policy</a>, which identifies
-                             how the Scratch Team uses, collects, and stores information
-                             it collects through the Services. By using Scratch, you
-                             additionally agree that you are comfortable with Scratch's
-                             Privacy Policy.
-                        </p>
-                        <p>
-                            1.3 Scratch is open to children and adults of all ages, and
-                             we ask that you keep this in mind when using the Scratch
-                             services. When you use Scratch, you agree to abide by the{' '}
-                             <a href="/community_guidelines">Scratch Community Guidelines</a>.
-                        </p>
-                        <p>
-                            1.4 The Scratch Team may change the Terms of Use from time to
-                             time. You can always find the latest version of the Terms of Use
-                             at <a href="/terms_of_use">http://scratch.mit.edu/terms_of_use</a>.
-                             The date of the most recent revisions will appear on this page.
-                             Your continued use of the Services constitutes your acceptance
-                             of any changes to or revisions of the Terms of Use.
-                        </p>
-                    </section>
-                    <section id="account-creation">
-                        <span className="nav-spacer"></span>
-                        <h3>2. Account Creation and Maintenance</h3>
-                        <p>
-                            2.1 In order to use some features of the Services, you will need to
-                             register with Scratch and create an account. Creating an account is
-                             optional, but without an account you will not be able to save or
-                             publish projects or comments on Scratch. When registering for a
-                             personal account, you will be asked to provide certain personal
-                             information, such as your email address, gender, birth month and
-                             year, and country. Please see Scratch's{' '}
-                             <a href="/privacy_policy">Privacy Policy</a> for Scratch's data
-                             retention and usage policies.
-                        </p>
-                        <p>
-                            2.2 You are responsible for keeping your password secret and your
-                             account secure. You are solely responsible for any use of your
-                             account, even if your account is used by another person. If any use
-                             of your account violates the Terms of Service, your account may be
-                             suspended or deleted.
-                        </p>
-                        <p>
-                            2.3 You may not use another person's Scratch account without permission.
-                        </p>
-                        <p>
-                            2.4 Account names cannot be changed. If you want a different account name,
-                             create a new account and copy your existing projects over by hand.
-                        </p>
-                        <p>
-                            2.5 If you have reason to believe that your account is no longer secure
-                             (for example, in the event of a loss, theft, or unauthorized disclosure
-                             of your password), promptly change your password. If you cannot access
-                             your account to change your password, notify us at{' '}
-                             <a href="mailto:help@scratch.mit.edu">help@scratch.mit.edu</a>.
-                        </p>
-                    </section>
-                    <section id="rules-of-usage">
-                        <span className="nav-spacer"></span>
-                        <h3>3. Rules of Usage</h3>
-                        <p>
-                            3.1 The Scratch Team supports freedom of expression. However, Scratch is
-                             intended for a wide audience, and some content is inappropriate for the
-                             Scratch community. You may not use the Scratch service in any way, that:
-                            <ol>
-                                <li>
-                                    Promotes bigotry, discrimination, hatred, or violence against any
-                                     individual or group;
-                                </li>
-                                <li>
-                                    Threatens, harasses, or intimidates any other person, whether that
-                                     person is a Scratch user or not;
-                                </li>
-                                <li>Contains foul language or personal attacks;</li>
-                                <li>Contains sexually explicit or graphically violent material;</li>
-                                <li>
-                                    Provides instructions on how to commit illegal activities or obtain
-                                     illegal products;
-                                </li>
-                                <li>
-                                    Except in connection with organizing Scratch day events, asks any
-                                     other user for personally identifying information, contact
-                                     information, or passwords; or
-                                </li>
-                                <li>
-                                    Exposes any others person's personally identifying information,
-                                     contact information, or passwords without that person's permission.
-                                </li>
-                            </ol>
-                        </p>
-                        <p>
-                            3.3 You agree to comply with all applicable laws and regulations when you use
-                             Scratch. You may not use Scratch in any unlawful way, including to harass,
-                             stalk, or defame any other person.
-                        </p>
-                        <p>
-                            3.4 You may not impersonate, imitate or pretend to be somebody else when using
-                             the Services.
-                        </p>
-                        <p>
-                            3.5 You agree not to use Scratch in any way intended to disrupt the service,
-                             gain unauthorized access to the service, or interfere with any other user's
-                             ability to use the service. Prohibited activities include, but are not limited
-                             to:
-                            <ol>
-                                <li>
-                                    Posting content deliberately designed to crash the Scratch
-                                     website or editor;
-                                </li>
-                                <li>
-                                    Linking to pages containing viruses or malware;
-                                </li>
-                                <li>
-                                    Using administrator passwords or pretending to be an administrator;
-                                </li>
-                                <li>
-                                    Repeatedly posting the same material, or "spamming";
-                                </li>
-                                <li>
-                                    Using alternate accounts or organizing voting groups to manipulate
-                                     site statistics, such as purposely trying to get on the "What the
-                                     Community is Loving/Remixing" rows of the front page.
-                                </li>
-                            </ol>
-                        </p>
-                        <p>
-                            3.6 Commercial use of Scratch, user-generated content, and support
-                             materials is permitted under the{' '}
-                             <a href="https://creativecommons.org/licenses/by-sa/2.0/">
-                             Creative Commons Attribution-ShareAlike 2.0 license</a>. However,
-                             the Scratch Team reserves the right to block any commercial use
-                             of Scratch that, in the Scratch Team's sole discretion, is harmful
-                             to the community. Harmful commercial use includes spamming or
-                             repeated advertisement through projects, comments, or forum posts.
-                        </p>
-                        <p>
-                            3.7 You agree not to post links to any content outside of the
-                            Scratch website, if to do so would violate any part of the Terms of Use.
-                        </p>
-                    </section>
-                    <section id="user-content">
-                        <span className="nav-spacer"></span>
-                        <h3>4. User-Generated Content and Licensing</h3>
-                        <p>
-                            4.1 For the purposes of the Terms of Use, "user-generated content"
-                             includes any projects, comments, forum posts, or links to third
-                             party websites that a user submits to Scratch.
-                        </p>
-                        <p>
-                            4.2 The Scratch Team encourages everyone to foster creativity by
-                             freely sharing code, art, music, and other works. However, we
-                             also understand the need for individuals and companies to protect
-                             their intellectual property rights. You are responsible for making
-                             sure you have the necessary rights, licenses, or permission for
-                             any user-generated content you submit to Scratch.
-                        </p>
-                        <p>
-                            4.3 All user-generated content you submit to Scratch is licensed
-                             to and through Scratch under the{' '}
-                             <a href="https://creativecommons.org/licenses/by-sa/2.0/">
-                             Creative Commons Attribution-ShareAlike 2.0 license</a>. This
-                             allows others to view and remix your content. This license also
-                             allows the Scratch Team to display, distribute, and reproduce
-                             your content on the Scratch website, through social media
-                             channels, and elsewhere. If you do not want to license your
-                             content under this license, then do not share it on Scratch.
-                        </p>
-                        <p>
-                            4.4 You may only submit user-generated projects that were created
-                             with (1) the Scratch website editor or (2) an unmodified copy of
-                             the Scratch editor compiled from the source code described in
-                             Section 5.3. You may not upload any projects that were created, by
-                             you or by anyone else, with a modified version of the Scratch editor.
-                        </p>
-                        <p>
-                            4.5 Although the Scratch Team requires all users to comply with
-                             these Terms of Use, some inappropriate user-generated content
-                             may be submitted and displayed on the Scratch website. You
-                             understand that when you use Scratch you may be exposed to
-                             user-generated content that you find objectionable or offensive.
-                             If you see any content that violates the Community Guidelines
-                             or Terms of Use, please let us know by using the "Report this"
-                             button. You only need to report an item once. The Scratch Team
-                             reviews reported content every day.
-                        </p>
-                        <p>
-                            4.6 In addition to reviewing reported user-generated content, the
-                             Scratch Team reserves the right, but is not obligated, to monitor
-                             all uses of the Scratch service. The Scratch Team may edit, move,
-                             or delete any content that violates the Terms of Use or Community
-                             Guidelines, without notice.
-                        </p>
-                        <p>
-                            4.7 All user-generated content is provided as-is. The Scratch Team
-                             makes no warranties about the accuracy or reliability of any
-                             user-generated content available through Scratch and does not
-                             endorse Scratch Day events or vet or verify information posted in
-                             connection with said events. The Scratch Team does not endorse any
-                             views, opinions, or advice expressed in user-generated content. You
-                             agree to relieve the Scratch Team of any and all liability arising
-                             from your user-generated content and from Scratch Day events you
-                             may organize or host.
-                        </p>
-                    </section>
-                    <section id="scratch-content">
-                        <span className="nav-spacer"></span>
-                        <h3>5. Scratch Content and Licensing</h3>
-                        <p>
-                            5.1 Except for any user-generated content, the Scratch Team owns and
-                             retains all rights in and to the Scratch code, the design,
-                             functionality, and architecture of Scratch, and any software or
-                             content provided through Scratch (collectively "the Scratch IP").
-                             If you want to use Scratch in a way that is not allowed by these
-                             Terms of Use, you must first contact the Scratch Team. Except for
-                             any rights explicitly granted under these Terms of Use, you are not
-                             granted any rights in and to any Scratch IP.
-                        </p>
-                        <p>
-                            5.2 Scratch provides support materials, including images, sounds,
-                             video, and sample code, to help users build projects. Support materials
-                             are licensed under the{' '}
-                             <a href="https://creativecommons.org/licenses/by-sa/2.0/">
-                             Creative Commons Attribution-ShareAlike 2.0 license</a>. You may also
-                             use screenshots of Scratch under the same license. Please note that
-                             this does not apply to materials that are also trademarked by the
-                             Scratch Team or other parties as described in parts 5.4 and 5.5, below.
-                             <br/>
-                             The Creative Commons Attribution-ShareAlike 2.0 license requires you to
-                             attribute any material you use to the original author. When you use
-                             Scratch support materials, or screenshots of the Scratch website,
-                             please use the following attribution: "Scratch is developed by the
-                             Lifelong Kindergarten Group at the MIT Media Lab. See
-                             http://scratch.mit.edu."
-                        </p>
-                        <p>
-                            5.3 The source code for Scratch 1.4 is available for download and subject
-                             to the copyright notice as indicated on the <a href="/info/faq">Scratch FAQ</a>
-                             {' '}page.
-                        </p>
-                        <p>
-                            5.4 The Scratch name, Scratch logo, Scratch Day logo, Scratch Cat, and Gobo
-                             are Trademarks owned by the Scratch Team. The MIT name and logo are Trademarks
-                             owned by the Massachusetts Institute of Technology. Unless you are licensed by
-                             Scratch under a specific licensing program or agreement, you may not use
-                             these logos to label, promote, or endorse any product or service. You may use
-                             the Scratch Logo to refer to the Scratch website and programming language.
-                        </p>
-                        <p>
-                            5.5 The Scratch support materials library may contain images and sounds that
-                             are trademarked by third parties. The fact that materials are included in
-                             the Scratch support materials library does not in any way limit or reduce
-                             intellectual property rights, including trademark rights, otherwise
-                             available to the materials' owners. Nothing in these Terms of Use or the
-                             Creative Commons 2.0 license will be construed to limit or reduce any
-                             party's rights in that party's valid trademarks. You may not use these
-                             materials to label, promote, or endorse any product or service. You are
-                             solely responsible for any violation of a third party's intellectual
-                             property rights caused by your misuse of these materials.
-                        </p>
-                    </section>
-                    <section id="dmca">
-                        <span className="nav-spacer"></span>
-                        <h3>6. Digital Millennium Copyright Act (DMCA)</h3>
-                        <p>
-                            6.1 If you are a copyright holder and believe that content on Scratch
-                             violates your rights, you may send a DMCA notification to{' '}
-                             <a href="mailto:copyright@scratch.mit.edu">copyright@scratch.mit.edu</a>.
-                             For more information, including the information that must be included
-                             in a DMCA notification, see our full <a href="/DMCA">DMCA Policy</a> and
-                             the text of the DMCA,{' '}
-                             <a href="http://www.law.cornell.edu/uscode/text/17/512">17 U.S.C. § 512</a>.
-                        </p>
-                        <p>
-                            6.2 If you are a Scratch user and you believe that your content did not
-                             constitute a copyright violation and was taken down in error, you may
-                             send a notification to{' '}
-                             <a href="mailto:copyright@scratch.mit.edu">copyright@scratch.mit.edu</a>.
-                             Please include:
-                            <ul>
-                                <li>Your Scratch username and email address;</li>
-                                <li>The specific content you believe was taken down in error; and</li>
-                                <li>
-                                    A brief statement of why you believe there was no copyright violation
-                                     (e.g., the content was not copyrighted, you had permission to use the
-                                     content, or your use of the content was a "fair use").
-                                </li>
-                            </ul>
-                        </p>
-                    </section>
-                    <section id="termination">
-                        <span className="nav-spacer"></span>
-                        <h3>7. Suspension and Termination of Accounts</h3>
-                        <p>
-                            7.1 Scratch has the right to suspend your account for violations of the
-                             Terms of Use or Community Guidelines. Repeat violators may have their
-                             account deleted. The Scratch Team reserves the sole right to determine
-                             what constitutes a violation of the Terms of Use or Community Guidelines.
-                             The Scratch Team also reserves the right to terminate any account used
-                             to circumvent prior enforcement of the Terms of Use.
-                        </p>
-                        <p>
-                            7.2 If you want to delete or temporarily disable your account, please
-                             email <a href="mailto:help@scratch.mit.edu">help@scratch.mit.edu</a>.
-                        </p>
-                    </section>
-                    <section id="third-party">
-                        <span className="nav-spacer"></span>
-                        <h3>8. Third Party Websites</h3>
-                        <p>
-                            8.1 Content on Scratch, including user-generated content, may include
-                             links to third party websites. The Scratch Team is not capable of
-                             reviewing or managing third party websites, and assumes no
-                             responsibility for the privacy practices, content, or functionality
-                             of third party websites. You agree to relieve the Scratch Team of
-                             any and all liability arising from third party websites.
-                        </p>
-                    </section>
-                    <section id="indemnification">
-                        <span className="nav-spacer"></span>
-                        <h3>9. Indemnification</h3>
-                        <p>
-                            You agree to indemnify MIT, the Scratch Team, the Scratch Foundation,
-                             and all their affiliates, employees, faculty members, fellows,
-                             students, agents, representatives, third party service providers,
-                             and members of their governing boards (all of which are "Scratch
-                             Entities"), and to defend and hold each of them harmless, from any
-                             and all claims and liabilities (including attorneys{'\''} fees) arising
-                             out of or related to your breach of the Terms of Service or your
-                             use of Scratch.
-                        </p>
-                        <p>
-                            For federal government agencies, provisions in the Terms of Use
-                             relating to Indemnification shall not apply to your Official Use,
-                             except to the extent expressly authorized by federal law. For
-                             state and local government agencies in the United States, Terms
-                             of Use relating to Indemnification shall apply to your Official
-                             Use only to the extent authorized by the laws of your jurisdiction.
-                        </p>
-                    </section>
-                    <section id="disclaimer">
-                        <span className="nav-spacer"></span>
-                        <h3>10. Disclaimer of Warranty</h3>
-                        <p><b>
-                            You acknowledge that you are using Scratch at your own risk. Scratch
-                             is provided "as is," and the Scratch Entities hereby expressly
-                             disclaim any and all warranties, express and implied, including but
-                             not limited to any warranties of accuracy, reliability, title,
-                             merchantability, non-infringement, fitness for a particular purpose
-                             or any other warranty, condition, guarantee or representation,
-                             whether oral, in writing or in electronic form, including but not
-                             limited to the accuracy or completeness of any information contained
-                             therein or provided by Scratch. Without limiting the foregoing, the
-                             Scratch Entities disclaim any and all warranties, express and implied,
-                             regarding user-generated content and Scratch Day events. The Scratch
-                             Entities and their third party service providers do not represent or
-                             warrant that access to Scratch will be uninterrupted or that there
-                             will be no failures, errors or omissions or loss of transmitted
-                             information, or that no viruses will be transmitted through Scratch
-                             services.
-                        </b></p>
-                    </section>
-                    <section id="liability">
-                        <span className="nav-spacer"></span>
-                        <h3>11. Limitation of Liability</h3>
-                        <p><b>
-                            The Scratch Entities shall not be liable to you or any third parties
-                             for any direct, indirect, special, consequential or punitive damages
-                             of any kind, regardless of the type of claim or the nature of the
-                             cause of action, even if the Scratch Team has been advised of the
-                             possibility of such damages. Without limiting the foregoing, the
-                             Scratch Entites shall have no liability to you or any third parties
-                             for damages or harms arising out of user-generated content or
-                             Scratch Day events.
-                        </b></p>
-                    </section>
-                    <section id="jurisdiction">
-                        <span className="nav-spacer"></span>
-                        <h3>12. Jurisdiction</h3>
-                        <p>
-                            Scratch is offered by the Scratch Team from its facilities in the United
-                             States. The Scratch Team makes no representations that Scratch is
-                             appropriate or available for use in other locations. Those who access
-                             or use Scratch are responsible for compliance with local law.
-                        </p>
-                    </section>
-                    <section id="choice-of-law">
-                        <span className="nav-spacer"></span>
-                        <h3>13. Choice of Law and Venue</h3>
-                        <p>
-                            You agree that these Terms of Use, for all purposes, shall be governed
-                             and construed in accordance with the laws of the Commonwealth of
-                             Massachusetts applicable to contracts to be wholly performed therein,
-                             and any action based on, relating to, or alleging a breach of the
-                             Terms of Use must be brought in a state or federal court in Suffolk
-                             County, Massachusetts. In addition, both parties agree to submit to
-                             the exclusive personal jurisdiction and venue of such courts.
-                        </p>
-                        <p>
-                            If you are a federal, state, or local government entity in the United
-                             States using Scratch in your official capacity and legally unable to
-                             accept the controlling law, jurisdiction or venue clauses above, then
-                             those clauses do not apply to you. For such U.S. federal government
-                             entities, these Terms and any action related thereto will be governed
-                             by the laws of the United States of America (without reference to
-                             conflict of laws) and, in the absence of federal law and to the extent
-                             permitted under federal law, the laws of the Commonwealth of
-                             Massachusetts (excluding choice of law).
-                        </p>
-                    </section>
-                    <section id="choice-of-language">
-                        <span className="nav-spacer"></span>
-                        <h3>14. Choice of Language</h3>
-                        <p>
-                            If the Scratch Team provides you with a translation of the English language
-                             version of these Terms of Use, the Privacy Policy, or any other policy,
-                             then you agree that the translation is provided for informational purposes
-                             only and does not modify the English language version. In the event of a
-                             conflict between a translation and the English version, the English version
-                             will govern.
-                        </p>
-                    </section>
-                    <section id="no-waiver">
-                        <span className="nav-spacer"></span>
-                        <h3>15. No Waiver</h3>
-                        <p>
-                            No waiver of any term of these Terms of Use shall be deemed a further or
-                             continuing waiver of such term or any other term, and the Scratch Team's
-                             failure to assert any right or provision under these Terms of Use shall
-                             not constitute a waiver of such right or provision.
-                        </p>
-                    </section>
-                    <section id="entire-agreement">
-                        <span className="nav-spacer"></span>
-                        <h3>16. Entire Agreement</h3>
-                        <p>
-                            This document, together with all appendices, constitutes the entire Terms
-                             of Use and supersedes all previous agreements with the Scratch Team relating
-                             to the use of Scratch. Revision date: 4 March 2015.
-                        </p>
-                    </section>
-                    <section id="appendix-a">
-                        <span className="nav-spacer"></span>
-                        <h3>Appendix A: Additional Terms for Scratch Day Website</h3>
-                        <p>
-                            The following additional terms also govern your access to and use of web
-                             pages hosted within day.scratch.mit.edu/ (collectively, the “Scratch Day
-                             Site”). All of the terms set forth in the general Terms of Use above also
-                             apply to the Scratch Day Site, unless we clearly state otherwise.
-                        </p>
-                        <h4>1. Privacy Policy</h4>
-                        <p>
-                            The Scratch Day Site Privacy Policy, not the Scratch Privacy Policy,
-                             describes how the Scratch Team uses, collects, and stores information
-                             it collects through the Scratch Day Site. By using the Scratch Day
-                             Site, you agree that you are comfortable with the Privacy Policy.
-                        </p>
-                        <h4>2. Account Creation and Maintenance</h4>
-                        <p>
-                            2.1 In order to post an event to the Scratch Day Site, you will need
-                             to register and create an account. This account is a separate account
-                             from your Scratch account. All registrants must be over 18 years of
-                             age. When registering for a personal account, you will be asked to
-                             provide certain personal information, such as your email address,
-                             first and last name, and Scratch username (optional). Please see the
-                             Scratch Day Site Privacy Policy for Scratch’s data retention and usage
-                             policies.
-                        </p>
-                        <p>
-                            2.2 You are responsible for keeping your password secret and your account
-                             secure. You are solely responsible for any use of your account, even if
-                             your account is used by another person. If any use of your account
-                             violates the Terms of Use, your account may be suspended or deleted.
-                        </p>
-                        <p>
-                            2.3 If you have reason to believe that your account is no longer secure
-                             (for example, in the event of a loss, theft, or unauthorized disclosure
-                             of your password), promptly change your password. If you cannot access
-                             your account to change your password, notify us at{' '}
-                             <a href="mailto:scratchday@media.mit.edu">scratchday@media.mit.edu</a>
-                        </p>
-                        <p>
-                            2.4 The terms set forth in this section apply to the Scratch Day Site.
-                             The Account Creation and Maintenance terms in the general Terms of Use
-                             do not apply to the Scratch Day Site.
-                        </p>
-                        <h4>3. No Endorsement</h4>
-                        <p>
-                            You understand that neither MIT, nor the Scratch Team, nor the Code to
-                             Learn Foundation endorses any Scratch Day event. If you are hosting a
-                             Scratch Day event, you may not state or imply that MIT, the Scratch
-                             Team, or the Code to Learn Foundation has endorsed your event.
-                        </p>
-                    </section>
-                    <section id="appendix-b">
-                        <span className="nav-spacer"></span>
-                        <h3>Appendix B: Additional Terms for ScratchX Website</h3>
-                        <p>
-                            The following additional terms also govern your access to and use of web
-                             pages hosted within scratchx.org (collectively, the “ScratchX Site”).
-                             All of the terms set forth in the general Terms of Use above also apply
-                             to the ScratchX Site, unless we clearly state otherwise.
-                        </p>
-                        <h4>1. ScratchX and GitHub</h4>
-                        <p>
-                            The ScratchX Site provides a platform for developers to link their
-                             experimental extensions to Scratch. However, we do not host those
-                             extensions or save them on the ScratchX site. All the extensions loaded
-                             on to ScratchX are hosted publicly on independent developers’ accounts
-                             on GitHub. Your use of GitHub is subject to GitHub’s Terms of Service
-                             and Privacy.
-                        </p>
-                        <h4>2. Privacy Policy</h4>
-                        <p>
-                            The ScratchX Site Privacy Policy, not the Scratch Privacy Policy,
-                             describes how the Scratch Team uses, collects, and stores information
-                             it collects through the ScratchX Site. By using the ScratchX Site, you
-                             agree to the terms of the Privacy Policy.
-                        </p>
-                        <h4>3. No Endorsement</h4>
-                        <p>
-                            By using ScratchX, you understand that neither MIT, nor the Scratch Team,
-                             nor the Code to Learn Foundation endorses any ScratchX experimental
-                             extension. If you are a developer linking to your own experimental
-                             extension via the ScratchX site, you may not state or imply that MIT,
-                             the Scratch Team, or the Code to Learn Foundation has endorsed your
-                             extension.
-                        </p>
-                    </section>
-                    <p><b>The Scratch Terms of Use was last updated: April 2016</b></p>
-                </div>
-                <nav>
+const Page = require('../../components/page/www/page.jsx');
+const render = require('../../lib/render.jsx');
+
+const Terms = () => (
+    <InformationPage title={'Scratch Terms of Use'}>
+        <div className="inner info-inner">
+            <section id="user-agreement">
+                <span className="nav-spacer" />
+                <h3>1. User Agreement</h3>
+                <p>
+                    1.1 These Terms of Use constitute an agreement between
+                    you and the Scratch Team that governs your use of{' '}
+                    <a href="https://scratch.mit.edu">scratch.mit.edu</a>{' '}
+                    and all associated services, including but not limited
+                    to the <a href="http://day.scratch.mit.edu">Scratch Day</a>{' '}
+                    and <a href="http://scratchx.org">ScratchX</a> websites
+                    (collectively &#34;Scratch&#34;). The Scratch Team is part of the
+                    Lifelong Kindergarten Group in the Media Laboratory at the
+                    Massachusetts Institute of Technology (&#34;MIT&#34;). Please
+                    read the Terms of Use carefully. By using Scratch you
+                    affirm that you have read, understood, and accepted
+                    the terms and conditions in the Terms of Use. If you
+                    do not agree with any of these conditions, please do not
+                    use Scratch.
+                </p>
+                <p>
+                    1.2 Your privacy is important to us. Please read our{' '}
+                    <a href="/privacy_policy">Privacy Policy</a>, which identifies
+                    how the Scratch Team uses, collects, and stores information
+                    it collects through the Services. By using Scratch, you
+                    additionally agree that you are comfortable with Scratch&#39;s
+                    Privacy Policy.
+                </p>
+                <p>
+                    1.3 Scratch is open to children and adults of all ages, and
+                    we ask that you keep this in mind when using the Scratch
+                    services. When you use Scratch, you agree to abide by the{' '}
+                    <a href="/community_guidelines">Scratch Community Guidelines</a>.
+                </p>
+                <p>
+                    1.4 The Scratch Team may change the Terms of Use from time to
+                    time. You can always find the latest version of the Terms of Use
+                    at <a href="/terms_of_use">http://scratch.mit.edu/terms_of_use</a>.
+                    The date of the most recent revisions will appear on this page.
+                    Your continued use of the Services constitutes your acceptance
+                    of any changes to or revisions of the Terms of Use.
+                </p>
+            </section>
+            <section id="account-creation">
+                <span className="nav-spacer" />
+                <h3>2. Account Creation and Maintenance</h3>
+                <p>
+                    2.1 In order to use some features of the Services, you will need to
+                    register with Scratch and create an account. Creating an account is
+                    optional, but without an account you will not be able to save or
+                    publish projects or comments on Scratch. When registering for a
+                    personal account, you will be asked to provide certain personal
+                    information, such as your email address, gender, birth month and
+                    year, and country. Please see Scratch&#39;s{' '}
+                    <a href="/privacy_policy">Privacy Policy</a> for Scratch&#39;s data
+                    retention and usage policies.
+                </p>
+                <p>
+                    2.2 You are responsible for keeping your password secret and your
+                    account secure. You are solely responsible for any use of your
+                    account, even if your account is used by another person. If any use
+                    of your account violates the Terms of Service, your account may be
+                    suspended or deleted.
+                </p>
+                <p>
+                    2.3 You may not use another person&#39;s Scratch account without permission.
+                </p>
+                <p>
+                    2.4 Account names cannot be changed. If you want a different account name,
+                     create a new account and copy your existing projects over by hand.
+                </p>
+                <p>
+                    2.5 If you have reason to believe that your account is no longer secure
+                    (for example, in the event of a loss, theft, or unauthorized disclosure
+                    of your password), promptly change your password. If you cannot access
+                    your account to change your password, notify us at{' '}
+                    <a href="mailto:help@scratch.mit.edu">help@scratch.mit.edu</a>.
+                </p>
+            </section>
+            <section id="rules-of-usage">
+                <span className="nav-spacer" />
+                <h3>3. Rules of Usage</h3>
+                <p>
+                    3.1 The Scratch Team supports freedom of expression. However, Scratch is
+                     intended for a wide audience, and some content is inappropriate for the
+                     Scratch community. You may not use the Scratch service in any way, that:
                     <ol>
-                        <li><a href="#appendix-a">Scratch Day Terms</a></li>
-                        <li><a href="#appendix-b">ScratchX Terms</a></li>
+                        <li>
+                            Promotes bigotry, discrimination, hatred, or violence against any
+                             individual or group;
+                        </li>
+                        <li>
+                            Threatens, harasses, or intimidates any other person, whether that
+                             person is a Scratch user or not;
+                        </li>
+                        <li>Contains foul language or personal attacks;</li>
+                        <li>Contains sexually explicit or graphically violent material;</li>
+                        <li>
+                            Provides instructions on how to commit illegal activities or obtain
+                             illegal products;
+                        </li>
+                        <li>
+                            Except in connection with organizing Scratch day events, asks any
+                             other user for personally identifying information, contact
+                             information, or passwords; or
+                        </li>
+                        <li>
+                            Exposes any others person&#39;s personally identifying information,
+                             contact information, or passwords without that person&#39;s permission.
+                        </li>
                     </ol>
-                </nav>
-            </InformationPage>
-        );
-    }
-});
+                </p>
+                <p>
+                    3.3 You agree to comply with all applicable laws and regulations when you use
+                     Scratch. You may not use Scratch in any unlawful way, including to harass,
+                     stalk, or defame any other person.
+                </p>
+                <p>
+                    3.4 You may not impersonate, imitate or pretend to be somebody else when using
+                     the Services.
+                </p>
+                <p>
+                    3.5 You agree not to use Scratch in any way intended to disrupt the service,
+                     gain unauthorized access to the service, or interfere with any other user&#39;s
+                     ability to use the service. Prohibited activities include, but are not limited
+                     to:
+                    <ol>
+                        <li>
+                            Posting content deliberately designed to crash the Scratch
+                             website or editor;
+                        </li>
+                        <li>
+                            Linking to pages containing viruses or malware;
+                        </li>
+                        <li>
+                            Using administrator passwords or pretending to be an administrator;
+                        </li>
+                        <li>
+                            Repeatedly posting the same material, or &#34;spamming&#34;;
+                        </li>
+                        <li>
+                            Using alternate accounts or organizing voting groups to manipulate
+                             site statistics, such as purposely trying to get on the &#34;What the
+                             Community is Loving/Remixing&#34; rows of the front page.
+                        </li>
+                    </ol>
+                </p>
+                <p>
+                    3.6 Commercial use of Scratch, user-generated content, and support
+                    materials is permitted under the{' '}
+                    <a href="https://creativecommons.org/licenses/by-sa/2.0/">
+                    Creative Commons Attribution-ShareAlike 2.0 license</a>. However,
+                    the Scratch Team reserves the right to block any commercial use
+                    of Scratch that, in the Scratch Team&#39;s sole discretion, is harmful
+                    to the community. Harmful commercial use includes spamming or
+                    repeated advertisement through projects, comments, or forum posts.
+                </p>
+                <p>
+                    3.7 You agree not to post links to any content outside of the
+                    Scratch website, if to do so would violate any part of the Terms of Use.
+                </p>
+            </section>
+            <section id="user-content">
+                <span className="nav-spacer" />
+                <h3>4. User-Generated Content and Licensing</h3>
+                <p>
+                    4.1 For the purposes of the Terms of Use, &#34;user-generated content&#34;
+                     includes any projects, comments, forum posts, or links to third
+                     party websites that a user submits to Scratch.
+                </p>
+                <p>
+                    4.2 The Scratch Team encourages everyone to foster creativity by
+                     freely sharing code, art, music, and other works. However, we
+                     also understand the need for individuals and companies to protect
+                     their intellectual property rights. You are responsible for making
+                     sure you have the necessary rights, licenses, or permission for
+                     any user-generated content you submit to Scratch.
+                </p>
+                <p>
+                    4.3 All user-generated content you submit to Scratch is licensed
+                    to and through Scratch under the{' '}
+                    <a href="https://creativecommons.org/licenses/by-sa/2.0/">
+                    Creative Commons Attribution-ShareAlike 2.0 license</a>. This
+                    allows others to view and remix your content. This license also
+                    allows the Scratch Team to display, distribute, and reproduce
+                    your content on the Scratch website, through social media
+                    channels, and elsewhere. If you do not want to license your
+                    content under this license, then do not share it on Scratch.
+                </p>
+                <p>
+                    4.4 You may only submit user-generated projects that were created
+                     with (1) the Scratch website editor or (2) an unmodified copy of
+                     the Scratch editor compiled from the source code described in
+                     Section 5.3. You may not upload any projects that were created, by
+                     you or by anyone else, with a modified version of the Scratch editor.
+                </p>
+                <p>
+                    4.5 Although the Scratch Team requires all users to comply with
+                     these Terms of Use, some inappropriate user-generated content
+                     may be submitted and displayed on the Scratch website. You
+                     understand that when you use Scratch you may be exposed to
+                     user-generated content that you find objectionable or offensive.
+                     If you see any content that violates the Community Guidelines
+                     or Terms of Use, please let us know by using the &#34;Report this&#34;
+                     button. You only need to report an item once. The Scratch Team
+                     reviews reported content every day.
+                </p>
+                <p>
+                    4.6 In addition to reviewing reported user-generated content, the
+                     Scratch Team reserves the right, but is not obligated, to monitor
+                     all uses of the Scratch service. The Scratch Team may edit, move,
+                     or delete any content that violates the Terms of Use or Community
+                     Guidelines, without notice.
+                </p>
+                <p>
+                    4.7 All user-generated content is provided as-is. The Scratch Team
+                     makes no warranties about the accuracy or reliability of any
+                     user-generated content available through Scratch and does not
+                     endorse Scratch Day events or vet or verify information posted in
+                     connection with said events. The Scratch Team does not endorse any
+                     views, opinions, or advice expressed in user-generated content. You
+                     agree to relieve the Scratch Team of any and all liability arising
+                     from your user-generated content and from Scratch Day events you
+                     may organize or host.
+                </p>
+            </section>
+            <section id="scratch-content">
+                <span className="nav-spacer" />
+                <h3>5. Scratch Content and Licensing</h3>
+                <p>
+                    5.1 Except for any user-generated content, the Scratch Team owns and
+                     retains all rights in and to the Scratch code, the design,
+                     functionality, and architecture of Scratch, and any software or
+                     content provided through Scratch (collectively &#34;the Scratch IP&#34;).
+                     If you want to use Scratch in a way that is not allowed by these
+                     Terms of Use, you must first contact the Scratch Team. Except for
+                     any rights explicitly granted under these Terms of Use, you are not
+                     granted any rights in and to any Scratch IP.
+                </p>
+                <p>
+                    5.2 Scratch provides support materials, including images, sounds,
+                    video, and sample code, to help users build projects. Support materials
+                    are licensed under the{' '}
+                    <a href="https://creativecommons.org/licenses/by-sa/2.0/">
+                    Creative Commons Attribution-ShareAlike 2.0 license</a>. You may also
+                    use screenshots of Scratch under the same license. Please note that
+                    this does not apply to materials that are also trademarked by the
+                    Scratch Team or other parties as described in parts 5.4 and 5.5, below.
+                    <br />
+                    The Creative Commons Attribution-ShareAlike 2.0 license requires you to
+                    attribute any material you use to the original author. When you use
+                    Scratch support materials, or screenshots of the Scratch website,
+                    please use the following attribution: &#34;Scratch is developed by the
+                    Lifelong Kindergarten Group at the MIT Media Lab. See
+                    http://scratch.mit.edu.&#34;
+                </p>
+                <p>
+                    5.3 The source code for Scratch 1.4 is available for download and subject
+                    to the copyright notice as indicated on the <a href="/info/faq">Scratch FAQ</a>
+                    {' '}page.
+                </p>
+                <p>
+                    5.4 The Scratch name, Scratch logo, Scratch Day logo, Scratch Cat, and Gobo
+                     are Trademarks owned by the Scratch Team. The MIT name and logo are Trademarks
+                     owned by the Massachusetts Institute of Technology. Unless you are licensed by
+                     Scratch under a specific licensing program or agreement, you may not use
+                     these logos to label, promote, or endorse any product or service. You may use
+                     the Scratch Logo to refer to the Scratch website and programming language.
+                </p>
+                <p>
+                    5.5 The Scratch support materials library may contain images and sounds that
+                     are trademarked by third parties. The fact that materials are included in
+                     the Scratch support materials library does not in any way limit or reduce
+                     intellectual property rights, including trademark rights, otherwise
+                     available to the materials&#39; owners. Nothing in these Terms of Use or the
+                     Creative Commons 2.0 license will be construed to limit or reduce any
+                     party&#39;s rights in that party&#39;s valid trademarks. You may not use these
+                     materials to label, promote, or endorse any product or service. You are
+                     solely responsible for any violation of a third party&#39;s intellectual
+                     property rights caused by your misuse of these materials.
+                </p>
+            </section>
+            <section id="dmca">
+                <span className="nav-spacer" />
+                <h3>6. Digital Millennium Copyright Act (DMCA)</h3>
+                <p>
+                    6.1 If you are a copyright holder and believe that content on Scratch
+                    violates your rights, you may send a DMCA notification to{' '}
+                    <a href="mailto:copyright@scratch.mit.edu">copyright@scratch.mit.edu</a>.
+                    For more information, including the information that must be included
+                    in a DMCA notification, see our full <a href="/DMCA">DMCA Policy</a> and
+                    the text of the DMCA,{' '}
+                    <a href="http://www.law.cornell.edu/uscode/text/17/512">17 U.S.C. § 512</a>.
+                </p>
+                <p>
+                    6.2 If you are a Scratch user and you believe that your content did not
+                    constitute a copyright violation and was taken down in error, you may
+                    send a notification to{' '}
+                    <a href="mailto:copyright@scratch.mit.edu">copyright@scratch.mit.edu</a>.
+                    Please include:
+                    <ul>
+                        <li>Your Scratch username and email address;</li>
+                        <li>The specific content you believe was taken down in error; and</li>
+                        <li>
+                            A brief statement of why you believe there was no copyright violation
+                            (e.g., the content was not copyrighted, you had permission to use the
+                            content, or your use of the content was a &#34;fair use&#34;).
+                        </li>
+                    </ul>
+                </p>
+            </section>
+            <section id="termination">
+                <span className="nav-spacer" />
+                <h3>7. Suspension and Termination of Accounts</h3>
+                <p>
+                    7.1 Scratch has the right to suspend your account for violations of the
+                     Terms of Use or Community Guidelines. Repeat violators may have their
+                     account deleted. The Scratch Team reserves the sole right to determine
+                     what constitutes a violation of the Terms of Use or Community Guidelines.
+                     The Scratch Team also reserves the right to terminate any account used
+                     to circumvent prior enforcement of the Terms of Use.
+                </p>
+                <p>
+                    7.2 If you want to delete or temporarily disable your account, please
+                     email <a href="mailto:help@scratch.mit.edu">help@scratch.mit.edu</a>.
+                </p>
+            </section>
+            <section id="third-party">
+                <span className="nav-spacer" />
+                <h3>8. Third Party Websites</h3>
+                <p>
+                    8.1 Content on Scratch, including user-generated content, may include
+                     links to third party websites. The Scratch Team is not capable of
+                     reviewing or managing third party websites, and assumes no
+                     responsibility for the privacy practices, content, or functionality
+                     of third party websites. You agree to relieve the Scratch Team of
+                     any and all liability arising from third party websites.
+                </p>
+            </section>
+            <section id="indemnification">
+                <span className="nav-spacer" />
+                <h3>9. Indemnification</h3>
+                <p>
+                    You agree to indemnify MIT, the Scratch Team, the Scratch Foundation,
+                     and all their affiliates, employees, faculty members, fellows,
+                     students, agents, representatives, third party service providers,
+                     and members of their governing boards (all of which are &#34;Scratch
+                     Entities&#34;), and to defend and hold each of them harmless, from any
+                     and all claims and liabilities (including attorneys{'\''} fees) arising
+                     out of or related to your breach of the Terms of Service or your
+                     use of Scratch.
+                </p>
+                <p>
+                    For federal government agencies, provisions in the Terms of Use
+                     relating to Indemnification shall not apply to your Official Use,
+                     except to the extent expressly authorized by federal law. For
+                     state and local government agencies in the United States, Terms
+                     of Use relating to Indemnification shall apply to your Official
+                     Use only to the extent authorized by the laws of your jurisdiction.
+                </p>
+            </section>
+            <section id="disclaimer">
+                <span className="nav-spacer" />
+                <h3>10. Disclaimer of Warranty</h3>
+                <p><b>
+                    You acknowledge that you are using Scratch at your own risk. Scratch
+                     is provided &#34;as is,&#34; and the Scratch Entities hereby expressly
+                     disclaim any and all warranties, express and implied, including but
+                     not limited to any warranties of accuracy, reliability, title,
+                     merchantability, non-infringement, fitness for a particular purpose
+                     or any other warranty, condition, guarantee or representation,
+                     whether oral, in writing or in electronic form, including but not
+                     limited to the accuracy or completeness of any information contained
+                     therein or provided by Scratch. Without limiting the foregoing, the
+                     Scratch Entities disclaim any and all warranties, express and implied,
+                     regarding user-generated content and Scratch Day events. The Scratch
+                     Entities and their third party service providers do not represent or
+                     warrant that access to Scratch will be uninterrupted or that there
+                     will be no failures, errors or omissions or loss of transmitted
+                     information, or that no viruses will be transmitted through Scratch
+                     services.
+                </b></p>
+            </section>
+            <section id="liability">
+                <span className="nav-spacer" />
+                <h3>11. Limitation of Liability</h3>
+                <p><b>
+                    The Scratch Entities shall not be liable to you or any third parties
+                     for any direct, indirect, special, consequential or punitive damages
+                     of any kind, regardless of the type of claim or the nature of the
+                     cause of action, even if the Scratch Team has been advised of the
+                     possibility of such damages. Without limiting the foregoing, the
+                     Scratch Entites shall have no liability to you or any third parties
+                     for damages or harms arising out of user-generated content or
+                     Scratch Day events.
+                </b></p>
+            </section>
+            <section id="jurisdiction">
+                <span className="nav-spacer" />
+                <h3>12. Jurisdiction</h3>
+                <p>
+                    Scratch is offered by the Scratch Team from its facilities in the United
+                     States. The Scratch Team makes no representations that Scratch is
+                     appropriate or available for use in other locations. Those who access
+                     or use Scratch are responsible for compliance with local law.
+                </p>
+            </section>
+            <section id="choice-of-law">
+                <span className="nav-spacer" />
+                <h3>13. Choice of Law and Venue</h3>
+                <p>
+                    You agree that these Terms of Use, for all purposes, shall be governed
+                     and construed in accordance with the laws of the Commonwealth of
+                     Massachusetts applicable to contracts to be wholly performed therein,
+                     and any action based on, relating to, or alleging a breach of the
+                     Terms of Use must be brought in a state or federal court in Suffolk
+                     County, Massachusetts. In addition, both parties agree to submit to
+                     the exclusive personal jurisdiction and venue of such courts.
+                </p>
+                <p>
+                    If you are a federal, state, or local government entity in the United
+                     States using Scratch in your official capacity and legally unable to
+                     accept the controlling law, jurisdiction or venue clauses above, then
+                     those clauses do not apply to you. For such U.S. federal government
+                     entities, these Terms and any action related thereto will be governed
+                     by the laws of the United States of America (without reference to
+                     conflict of laws) and, in the absence of federal law and to the extent
+                     permitted under federal law, the laws of the Commonwealth of
+                     Massachusetts (excluding choice of law).
+                </p>
+            </section>
+            <section id="choice-of-language">
+                <span className="nav-spacer" />
+                <h3>14. Choice of Language</h3>
+                <p>
+                    If the Scratch Team provides you with a translation of the English language
+                     version of these Terms of Use, the Privacy Policy, or any other policy,
+                     then you agree that the translation is provided for informational purposes
+                     only and does not modify the English language version. In the event of a
+                     conflict between a translation and the English version, the English version
+                     will govern.
+                </p>
+            </section>
+            <section id="no-waiver">
+                <span className="nav-spacer" />
+                <h3>15. No Waiver</h3>
+                <p>
+                    No waiver of any term of these Terms of Use shall be deemed a further or
+                     continuing waiver of such term or any other term, and the Scratch Team&#39;s
+                     failure to assert any right or provision under these Terms of Use shall
+                     not constitute a waiver of such right or provision.
+                </p>
+            </section>
+            <section id="entire-agreement">
+                <span className="nav-spacer" />
+                <h3>16. Entire Agreement</h3>
+                <p>
+                    This document, together with all appendices, constitutes the entire Terms
+                     of Use and supersedes all previous agreements with the Scratch Team relating
+                     to the use of Scratch. Revision date: 4 March 2015.
+                </p>
+            </section>
+            <section id="appendix-a">
+                <span className="nav-spacer" />
+                <h3>Appendix A: Additional Terms for Scratch Day Website</h3>
+                <p>
+                    The following additional terms also govern your access to and use of web
+                     pages hosted within day.scratch.mit.edu/ (collectively, the “Scratch Day
+                     Site”). All of the terms set forth in the general Terms of Use above also
+                     apply to the Scratch Day Site, unless we clearly state otherwise.
+                </p>
+                <h4>1. Privacy Policy</h4>
+                <p>
+                    The Scratch Day Site Privacy Policy, not the Scratch Privacy Policy,
+                     describes how the Scratch Team uses, collects, and stores information
+                     it collects through the Scratch Day Site. By using the Scratch Day
+                     Site, you agree that you are comfortable with the Privacy Policy.
+                </p>
+                <h4>2. Account Creation and Maintenance</h4>
+                <p>
+                    2.1 In order to post an event to the Scratch Day Site, you will need
+                     to register and create an account. This account is a separate account
+                     from your Scratch account. All registrants must be over 18 years of
+                     age. When registering for a personal account, you will be asked to
+                     provide certain personal information, such as your email address,
+                     first and last name, and Scratch username (optional). Please see the
+                     Scratch Day Site Privacy Policy for Scratch’s data retention and usage
+                     policies.
+                </p>
+                <p>
+                    2.2 You are responsible for keeping your password secret and your account
+                     secure. You are solely responsible for any use of your account, even if
+                     your account is used by another person. If any use of your account
+                     violates the Terms of Use, your account may be suspended or deleted.
+                </p>
+                <p>
+                    2.3 If you have reason to believe that your account is no longer secure
+                    (for example, in the event of a loss, theft, or unauthorized disclosure
+                    of your password), promptly change your password. If you cannot access
+                    your account to change your password, notify us at{' '}
+                    <a href="mailto:scratchday@media.mit.edu">scratchday@media.mit.edu</a>
+                </p>
+                <p>
+                    2.4 The terms set forth in this section apply to the Scratch Day Site.
+                    The Account Creation and Maintenance terms in the general Terms of Use
+                    do not apply to the Scratch Day Site.
+                </p>
+                <h4>3. No Endorsement</h4>
+                <p>
+                    You understand that neither MIT, nor the Scratch Team, nor the Code to
+                     Learn Foundation endorses any Scratch Day event. If you are hosting a
+                     Scratch Day event, you may not state or imply that MIT, the Scratch
+                     Team, or the Code to Learn Foundation has endorsed your event.
+                </p>
+            </section>
+            <section id="appendix-b">
+                <span className="nav-spacer" />
+                <h3>Appendix B: Additional Terms for ScratchX Website</h3>
+                <p>
+                    The following additional terms also govern your access to and use of web
+                     pages hosted within scratchx.org (collectively, the “ScratchX Site”).
+                     All of the terms set forth in the general Terms of Use above also apply
+                     to the ScratchX Site, unless we clearly state otherwise.
+                </p>
+                <h4>1. ScratchX and GitHub</h4>
+                <p>
+                    The ScratchX Site provides a platform for developers to link their
+                     experimental extensions to Scratch. However, we do not host those
+                     extensions or save them on the ScratchX site. All the extensions loaded
+                     on to ScratchX are hosted publicly on independent developers’ accounts
+                     on GitHub. Your use of GitHub is subject to GitHub’s Terms of Service
+                     and Privacy.
+                </p>
+                <h4>2. Privacy Policy</h4>
+                <p>
+                    The ScratchX Site Privacy Policy, not the Scratch Privacy Policy,
+                     describes how the Scratch Team uses, collects, and stores information
+                     it collects through the ScratchX Site. By using the ScratchX Site, you
+                     agree to the terms of the Privacy Policy.
+                </p>
+                <h4>3. No Endorsement</h4>
+                <p>
+                    By using ScratchX, you understand that neither MIT, nor the Scratch Team,
+                     nor the Code to Learn Foundation endorses any ScratchX experimental
+                     extension. If you are a developer linking to your own experimental
+                     extension via the ScratchX site, you may not state or imply that MIT,
+                     the Scratch Team, or the Code to Learn Foundation has endorsed your
+                     extension.
+                </p>
+            </section>
+            <p><b>The Scratch Terms of Use was last updated: April 2016</b></p>
+        </div>
+        <nav>
+            <ol>
+                <li><a href="#appendix-a">Scratch Day Terms</a></li>
+                <li><a href="#appendix-b">ScratchX Terms</a></li>
+            </ol>
+        </nav>
+    </InformationPage>
+);
 
 render(<Page><Terms /></Page>, document.getElementById('app'));
diff --git a/src/views/tips/tips.jsx b/src/views/tips/tips.jsx
index 12012f531..3c0cd7e29 100644
--- a/src/views/tips/tips.jsx
+++ b/src/views/tips/tips.jsx
@@ -1,73 +1,95 @@
-var React = require('react');
-var injectIntl = require('react-intl').injectIntl;
-var FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
-var FormattedMessage = require('react-intl').FormattedMessage;
-var render = require('../../lib/render.jsx');
+const bindAll = require('lodash.bindall');
+const FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
+const FormattedMessage = require('react-intl').FormattedMessage;
+const injectIntl = require('react-intl').injectIntl;
+const intlShape = require('react-intl').intlShape;
+const React = require('react');
 
-var MasonryGrid = require('../../components/masonrygrid/masonrygrid.jsx');
-var Page = require('../../components/page/www/page.jsx');
-var Button = require('../../components/forms/button.jsx');
-var TitleBanner = require('../../components/title-banner/title-banner.jsx');
-var FlexRow = require('../../components/flex-row/flex-row.jsx');
-var TTTTile = require('../../components/ttt-tile/ttt-tile.jsx');
-var TTTModal = require('../../components/modal/ttt/modal.jsx');
-var Tiles = require('./ttt.json');
+const Button = require('../../components/forms/button.jsx');
+const FlexRow = require('../../components/flex-row/flex-row.jsx');
+const MasonryGrid = require('../../components/masonrygrid/masonrygrid.jsx');
+const TitleBanner = require('../../components/title-banner/title-banner.jsx');
+const TTTModal = require('../../components/modal/ttt/modal.jsx');
+const TTTTile = require('../../components/ttt-tile/ttt-tile.jsx');
+
+const Page = require('../../components/page/www/page.jsx');
+const render = require('../../lib/render.jsx');
+
+const Tiles = require('./ttt.json');
 
 require('./tips.scss');
 
-var Tips = injectIntl(React.createClass({
-    type: 'Tips',
-    getInitialState: function () {
-        return {currentTile: Tiles[0], TTTModalOpen: false};
-    },
-    showTTTModal: function (tile) {
+class Tips extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleShowTTTModal',
+            'handleHideTTTModal',
+            'renderTTTTiles'
+        ]);
+        this.state = {
+            currentTile: Tiles[0],
+            TTTModalOpen: false
+        };
+    }
+    handleShowTTTModal (tile) {
         // expects translated tile
-        this.setState({currentTile: tile, TTTModalOpen: true});
-    },
-    hideTTTModal: function () {
-        this.setState({TTTModalOpen: false});
-    },
-    renderTTTTiles: function () {
-        var formatMessage = this.props.intl.formatMessage;
-        var translatedTile = {};
+        this.setState({
+            currentTile: tile,
+            TTTModalOpen: true
+        });
+    }
+    handleHideTTTModal () {
+        this.setState({
+            TTTModalOpen: false
+        });
+    }
+    renderTTTTiles () {
+        let translatedTile = {};
 
-        return Tiles.map(function (tile, key) {
+        return Tiles.map((tile, key) => {
             translatedTile = {
-                title: formatMessage({id: tile.title}),
-                description: formatMessage({id: tile.description}),
-                tutorialLoc: tile.tutorialLoc,
-                activityLoc: formatMessage({id: tile.activityLoc}),
-                guideLoc: formatMessage({id: tile.guideLoc}),
+                activityLoc: this.props.intl.formatMessage({id: tile.activityLoc}),
+                bannerUrl: tile.bannerUrl,
+                description: this.props.intl.formatMessage({id: tile.description}),
+                guideLoc: this.props.intl.formatMessage({id: tile.guideLoc}),
                 thumbUrl: tile.thumbUrl,
-                bannerUrl: tile.bannerUrl
+                title: this.props.intl.formatMessage({id: tile.title}),
+                tutorialLoc: tile.tutorialLoc
             };
-            return (<TTTTile
-                key={key}
-                onGuideClick={this.showTTTModal.bind(this, translatedTile)}
-                {...translatedTile}/>);
-        }, this); // don't forget to pass 'this' into map function
-    },
-    render: function () {
-        var formatMessage = this.props.intl.formatMessage;
+            return (
+                <TTTTile
+                    key={key}
+                    onGuideClick={() => { // eslint-disable-line react/jsx-no-bind
+                        this.handleShowTTTModal(translatedTile);
+                    }}
+                    {...translatedTile}
+                />
+            );
+        }); // don't forget to pass 'this' into map function
+    }
+    render () {
         return (
             <div className="tips">
                 <TitleBanner className="masthead mod-blue-bg">
                     <h1 className="title-banner-h1">
-                        <FormattedMessage id="tips.title"/>
+                        <FormattedMessage id="tips.title" />
                     </h1>
                     <p className="intro title-banner-p">
                         <FormattedHTMLMessage
                             id="tips.subTitle"
                             values={{
-                                GettingStartedPDF: formatMessage({id: 'guides.Getting-Started-Guide-Scratch2Link'})
+                                GettingStartedPDF: this.props.intl.formatMessage({
+                                    id: 'guides.Getting-Started-Guide-Scratch2Link'
+                                })
                             }}
                         />
                     </p>
                     <p className="title-banner-p">
                         <a href="/projects/editor/?tip_bar=getStarted">
                             <Button className="tips-button getting-started-button">
-                                <img src="/images/tips/blocks-icon.svg"/>
-                                <FormattedMessage id="tips.tryGettingStarted"/>
+                                <img src="/images/tips/blocks-icon.svg" />
+                                <FormattedMessage id="tips.tryGettingStarted" />
                             </Button>
                         </a>
                     </p>
@@ -77,10 +99,10 @@ var Tips = injectIntl(React.createClass({
                     <section className="ttt-section">
                         <div className="ttt-head">
                             <h2>
-                                <FormattedMessage id="tips.tttHeader"/>
+                                <FormattedMessage id="tips.tttHeader" />
                             </h2>
                             <p>
-                                <FormattedHTMLMessage id="tips.tttBody"/>
+                                <FormattedHTMLMessage id="tips.tttBody" />
                             </p>
                         </div>
                         <MasonryGrid >
@@ -88,62 +110,78 @@ var Tips = injectIntl(React.createClass({
                         </MasonryGrid>
                         <TTTModal
                             isOpen={this.state.TTTModalOpen}
-                            onRequestClose={this.hideTTTModal}
-                            {...this.state.currentTile}/>
+                            onRequestClose={this.handleHideTTTModal}
+                            {...this.state.currentTile}
+                        />
                     </section>
                 </div>
                 <div className="tips-resources">
                     <div className="inner">
-                        <FlexRow as="section" className="tips-info-section cards-info">
+                        <FlexRow
+                            as="section"
+                            className="tips-info-section cards-info"
+                        >
                             <div className="tips-info-body">
                                 <h2>
-                                    <FormattedMessage id="tips.cardsHeader"/>
+                                    <FormattedMessage id="tips.cardsHeader" />
                                 </h2>
                                 <p>
-                                    <FormattedHTMLMessage id="tips.cardsBody"/>
+                                    <FormattedHTMLMessage id="tips.cardsBody" />
                                 </p>
                                 <p className="tips-cards-buttons">
-                                    <a href={formatMessage({id: 'cards.ScratchCardsAllLink'})}>
+                                    <a
+                                        href={this.props.intl.formatMessage({
+                                            id: 'cards.ScratchCardsAllLink'
+                                        })}
+                                    >
                                         <Button className="tips-button">
-                                            <FormattedMessage id="tips.cardsDownload"/>
+                                            <FormattedMessage id="tips.cardsDownload" />
                                         </Button>
                                     </a>
                                     <a
                                         href="https://scratch-foundation.myshopify.com/collections/all-products/products/scratch-coding-cards-creative-coding-activities-for-kids"
-                                        target="_blank">
+                                        rel="noopener noreferrer"
+                                        target="_blank"
+                                    >
                                         <Button className="tips-button purchase-button">
-                                            <FormattedMessage id="tips.cardsPurchase"/>
-                                            <img src="/images/tips/arrow-icon.svg"/>
+                                            <FormattedMessage id="tips.cardsPurchase" />
+                                            <img src="/images/tips/arrow-icon.svg" />
                                         </Button>
                                     </a>
                                 </p>
                             </div>
                             <div className="tips-info-body tips-illustration">
-                                <img src="/images/tips/cards-illustration.svg"/>
+                                <img src="/images/tips/cards-illustration.svg" />
                             </div>
                         </FlexRow>
                     </div>
                 </div>
                 <div className="inner">
-                    <div className="tips-divider"></div>
+                    <div className="tips-divider" />
                 </div>
                 <div className="tips-resources">
                     <div className="inner">
-                        <FlexRow as="section" className="tips-info-section">
+                        <FlexRow
+                            as="section"
+                            className="tips-info-section"
+                        >
                             <div className="tips-info-body tips-illustration">
-                                <img src="/images/tips/project-illustration.svg" className="mod-flow-left"/>
+                                <img
+                                    className="mod-flow-left"
+                                    src="/images/tips/project-illustration.svg"
+                                />
                             </div>
                             <div className="tips-info-body">
                                 <h2>
-                                    <FormattedMessage id="tips.starterProjectsHeader"/>
+                                    <FormattedMessage id="tips.starterProjectsHeader" />
                                 </h2>
                                 <p>
-                                    <FormattedHTMLMessage id="tips.starterProjectsBody"/>
+                                    <FormattedHTMLMessage id="tips.starterProjectsBody" />
                                 </p>
                                 <p>
                                     <a href="/starter_projects">
                                         <Button className="tips-button">
-                                            <FormattedMessage id="tips.starterProjectsPlay"/>
+                                            <FormattedMessage id="tips.starterProjectsPlay" />
                                         </Button>
                                     </a>
                                 </p>
@@ -152,40 +190,46 @@ var Tips = injectIntl(React.createClass({
                     </div>
                 </div>
                 <div className="inner">
-                        <FlexRow
-                            as="section"
-                            className="tips-info-section mod-align-top"
-                        >
-                            <div className="tips-info-body mod-narrow">
-                                <img
-                                    src="/images/tips/download-icon.svg"
-                                    className="tips-icon"
-                                />
-                                <h3>
-                                    <FormattedMessage id="tips.offlineEditorHeader"/>
-                                </h3>
-                                <p>
-                                    <FormattedHTMLMessage id="tips.offlineEditorBody"/>
-                                </p>
-                            </div>
-                            <div className="tips-info-body mod-narrow">
-                                <img
-                                    src="/images/tips/question-icon.svg"
-                                    className="tips-icon"
-                                />
-                                <h3>
-                                    <FormattedMessage id="tips.questionsHeader"/>
-                                </h3>
-                                <p>
-                                    <FormattedHTMLMessage id="tips.questionsBody"/>
-                                </p>
-                            </div>
-                        </FlexRow>
+                    <FlexRow
+                        as="section"
+                        className="tips-info-section mod-align-top"
+                    >
+                        <div className="tips-info-body mod-narrow">
+                            <img
+                                className="tips-icon"
+                                src="/images/tips/download-icon.svg"
+                            />
+                            <h3>
+                                <FormattedMessage id="tips.offlineEditorHeader" />
+                            </h3>
+                            <p>
+                                <FormattedHTMLMessage id="tips.offlineEditorBody" />
+                            </p>
+                        </div>
+                        <div className="tips-info-body mod-narrow">
+                            <img
+                                className="tips-icon"
+                                src="/images/tips/question-icon.svg"
+                            />
+                            <h3>
+                                <FormattedMessage id="tips.questionsHeader" />
+                            </h3>
+                            <p>
+                                <FormattedHTMLMessage id="tips.questionsBody" />
+                            </p>
+                        </div>
+                    </FlexRow>
                 </div>
             </div>
         );
     }
-}));
+}
+
+Tips.propTypes = {
+    intl: intlShape
+};
+
+const WrappedTips = injectIntl(Tips);
 
 render(
-    <Page><Tips/></Page>, document.getElementById('app'));
+    <Page><WrappedTips /></Page>, document.getElementById('app'));
diff --git a/src/views/wedo2/wedo2.jsx b/src/views/wedo2/wedo2.jsx
index 7457f40eb..e455f0732 100644
--- a/src/views/wedo2/wedo2.jsx
+++ b/src/views/wedo2/wedo2.jsx
@@ -1,142 +1,155 @@
-var FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
-var FormattedMessage = require('react-intl').FormattedMessage;
-var React = require('react');
+const FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
+const FormattedMessage = require('react-intl').FormattedMessage;
+const React = require('react');
 
-var Page = require('../../components/page/www/page.jsx');
-var render = require('../../lib/render.jsx');
+const Page = require('../../components/page/www/page.jsx');
+const render = require('../../lib/render.jsx');
 
 require('./wedo2.scss');
 
-var Wedo2 = React.createClass({
-    type: 'wedo2',
-    render: function () {
-        return (
-            <div className="wedo">
-                <div className="top-banner">
-                    <div className="inner">
-                        <div className="columns2">
-                            <div className="banner-text">
-                                <h2>LEGO WeDo 2.0 &amp; Scratch</h2>
-                                <p className="intro">
-                                    <FormattedMessage id='wedo2.intro' />
-                                </p>
-                            </div>
-                            <div className="banner-photo">
-                                <img src="/images/wedo/wedo-milo.png" />
-                            </div>
-                        </div>
-                     </div>
-                </div>
-
-                <div className="inner">
-                    <section id="getting-started">
-                        <h3>
-                            <FormattedMessage id='wedo2.getStarted' />
-                        </h3>
-                        <p className="callout">
-                            <FormattedMessage id='wedo2.requirement' />
+const Wedo2 = () => (
+    <div className="wedo">
+        <div className="top-banner">
+            <div className="inner">
+                <div className="columns2">
+                    <div className="banner-text">
+                        <h2>LEGO WeDo 2.0 &amp; Scratch</h2>
+                        <p className="intro">
+                            <FormattedMessage id="wedo2.intro" />
                         </p>
-                        <div className="columns3">
-                            <div className="column">
-                                <img src="/images/wedo/download-device-manager.png" />
-                                <h4>
-                                    <FormattedMessage id='wedo2.installTitle' />
-                                </h4>
-                                <p>
-                                    <FormattedHTMLMessage id='wedo2.installText' />
-                                    <br />
-                                    <a href="https://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=1084869222&mt=12">
-                                        <FormattedMessage id='wedo2.downloadMac' />
-                                    </a>
-                                    <br />
-                                    <a href="https://downloads.scratch.mit.edu/device-manager/ScratchDeviceManager-1.1.0.exe">
-                                        <FormattedMessage id='wedo2.downloadWin' />
-                                    </a>
-                                </p>
-                            </div>
-                            <div className="column">
-                                <img src="/images/wedo/set-up.png" />
-                                <h4>
-                                    <FormattedMessage id='wedo2.setupTitle' />
-                                </h4>
-                                <p>
-                                    <FormattedHTMLMessage id='wedo2.setupText' />
-                                </p>
-                            </div>
-                            <div className="column">
-                                <img src="/images/wedo/create-and-share.png" />
-                                <h4>
-                                    <FormattedMessage id='wedo2.createTitle' />
-                                </h4>
-                                <p>
-                                    <FormattedMessage id='wedo2.createText' />
-                                </p>
-                            </div>
-                        </div>
-                    </section>
-                </div>
-
-                <div className="banner">
-                    <div className="inner" id="starter-projects">
-                        <h3>
-                            <FormattedMessage id='wedo2.starterProjects' />
-                        </h3>
-                        <div className="project-list">
-                            <a href="/projects/101037564/?tip_bar=ext2#editor">
-                                <div className="project-card">
-                                    <img src="/images/wedo/motor.png" alt="" />
-                                    <p>
-                                        <FormattedMessage id='wedo2.starterMotor' />
-                                    </p>
-                                </div>
-                            </a>
-                            <a href="/projects/101038249/?tip_bar=ext2#editor">
-                                <div className="project-card">
-                                    <img src="/images/wedo/distance.png" alt="" />
-                                    <p>
-                                        <FormattedMessage id='wedo2.starterDistance' />
-                                    </p>
-                                </div>
-                            </a>
-                            <a href="/projects/101033190/?tip_bar=ext2#editor">
-                                <div className="project-card">
-                                    <img src="/images/wedo/tilt.png" alt="" />
-                                    <p>
-                                        <FormattedMessage id='wedo2.starterTilt' />
-                                    </p>
-                                </div>
-                            </a>
-                        </div>
+                    </div>
+                    <div className="banner-photo">
+                        <img src="/images/wedo/wedo-milo.png" />
                     </div>
                 </div>
+            </div>
+        </div>
 
-                <div className="inner">
-                    <section>
-                        <h3>
-                            <FormattedMessage id='wedo2.versionTitle' />
-                        </h3>
+        <div className="inner">
+            <section id="getting-started">
+                <h3>
+                    <FormattedMessage id="wedo2.getStarted" />
+                </h3>
+                <p className="callout">
+                    <FormattedMessage id="wedo2.requirement" />
+                </p>
+                <div className="columns3">
+                    <div className="column">
+                        <img src="/images/wedo/download-device-manager.png" />
+                        <h4>
+                            <FormattedMessage id="wedo2.installTitle" />
+                        </h4>
                         <p>
-                            <FormattedMessage id='wedo2.versionText' />
+                            <FormattedHTMLMessage id="wedo2.installText" />
+                            <br />
+                            <a href="https://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=1084869222&mt=12">
+                                <FormattedMessage id="wedo2.downloadMac" />
+                            </a>
+                            <br />
+                            <a href="https://downloads.scratch.mit.edu/device-manager/ScratchDeviceManager-1.1.0.exe">
+                                <FormattedMessage id="wedo2.downloadWin" />
+                            </a>
                         </p>
-                        <div className="device-card">
-                            <h4>LEGO WeDo 1.0 Hub</h4>
-                            <img src="/images/wedo/wedo1.png" alt="LEGO WeDo 1.0 Hub" />
-                            <a href="/projects/editor/?tip_bar=ext1">
-                                <FormattedMessage id='wedo2.wedo1SetupInstructions' />
-                            </a>
+                    </div>
+                    <div className="column">
+                        <img src="/images/wedo/set-up.png" />
+                        <h4>
+                            <FormattedMessage id="wedo2.setupTitle" />
+                        </h4>
+                        <p>
+                            <FormattedHTMLMessage id="wedo2.setupText" />
+                        </p>
+                    </div>
+                    <div className="column">
+                        <img src="/images/wedo/create-and-share.png" />
+                        <h4>
+                            <FormattedMessage id="wedo2.createTitle" />
+                        </h4>
+                        <p>
+                            <FormattedMessage id="wedo2.createText" />
+                        </p>
+                    </div>
+                </div>
+            </section>
+        </div>
+
+        <div className="banner">
+            <div
+                className="inner"
+                id="starter-projects"
+            >
+                <h3>
+                    <FormattedMessage id="wedo2.starterProjects" />
+                </h3>
+                <div className="project-list">
+                    <a href="/projects/101037564/?tip_bar=ext2#editor">
+                        <div className="project-card">
+                            <img
+                                alt=""
+                                src="/images/wedo/motor.png"
+                            />
+                            <p>
+                                <FormattedMessage id="wedo2.starterMotor" />
+                            </p>
                         </div>
-                        <div className="device-card">
-                            <h4>LEGO WeDo 2.0 Hub</h4>
-                            <img src="/images/wedo/wedo2.png" alt="LEGO WeDo 2.0 Hub" />
-                            <a href="/projects/editor/?tip_bar=ext2">
-                                <FormattedMessage id='wedo2.wedo2SetupInstructions' />
-                            </a>
+                    </a>
+                    <a href="/projects/101038249/?tip_bar=ext2#editor">
+                        <div className="project-card">
+                            <img
+                                alt=""
+                                src="/images/wedo/distance.png"
+                            />
+                            <p>
+                                <FormattedMessage id="wedo2.starterDistance" />
+                            </p>
                         </div>
-                    </section>
+                    </a>
+                    <a href="/projects/101033190/?tip_bar=ext2#editor">
+                        <div className="project-card">
+                            <img
+                                alt=""
+                                src="/images/wedo/tilt.png"
+                            />
+                            <p>
+                                <FormattedMessage id="wedo2.starterTilt" />
+                            </p>
+                        </div>
+                    </a>
                 </div>
             </div>
-        );
-    }
-});
+        </div>
+
+        <div className="inner">
+            <section>
+                <h3>
+                    <FormattedMessage id="wedo2.versionTitle" />
+                </h3>
+                <p>
+                    <FormattedMessage id="wedo2.versionText" />
+                </p>
+                <div className="device-card">
+                    <h4>LEGO WeDo 1.0 Hub</h4>
+                    <img
+                        alt="LEGO WeDo 1.0 Hub"
+                        src="/images/wedo/wedo1.png"
+                    />
+                    <a href="/projects/editor/?tip_bar=ext1">
+                        <FormattedMessage id="wedo2.wedo1SetupInstructions" />
+                    </a>
+                </div>
+                <div className="device-card">
+                    <h4>LEGO WeDo 2.0 Hub</h4>
+                    <img
+                        alt="LEGO WeDo 2.0 Hub"
+                        src="/images/wedo/wedo2.png"
+                    />
+                    <a href="/projects/editor/?tip_bar=ext2">
+                        <FormattedMessage id="wedo2.wedo2SetupInstructions" />
+                    </a>
+                </div>
+            </section>
+        </div>
+    </div>
+);
 
 render(<Page><Wedo2 /></Page>, document.getElementById('app'));
diff --git a/test/helpers/selenium-helpers.js b/test/helpers/selenium-helpers.js
index a850a6aac..2b55e2a4d 100644
--- a/test/helpers/selenium-helpers.js
+++ b/test/helpers/selenium-helpers.js
@@ -49,6 +49,7 @@ const getLogs = (whitelist) => {
                     }
                     return true;
                 }
+                return true;
             });
         });
 };
diff --git a/test/integration/smoke-testing/test_footer_links.js b/test/integration/smoke-testing/test_footer_links.js
index 330b2d548..da06e54f7 100644
--- a/test/integration/smoke-testing/test_footer_links.js
+++ b/test/integration/smoke-testing/test_footer_links.js
@@ -4,7 +4,7 @@
  * Test cases: https://github.com/LLK/scratch-www/wiki/Most-Important-Workflows
  */
  
-var tap = require('tap');
+const tap = require('tap');
 
 const {
     driver,
@@ -12,107 +12,109 @@ const {
 } = require('../../helpers/selenium-helpers.js');
 
 // Selenium's promise driver will be deprecated, so we should not rely on it
-webdriver.SELENIUM_PROMISE_MANAGER=0;
+webdriver.SELENIUM_PROMISE_MANAGER = 0;
 
-var rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
+const rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
 
-//timeout for each test; timeout for suite set at command line level
-var options = { timeout: 30000 };
+// timeout for each test; timeout for suite set at command line level
+const options = {timeout: 30000};
 
-//number of tests in the plan
+// number of tests in the plan
 tap.plan(25);
 
 tap.tearDown(function () {
-    //quit the instance of the browser
+    // quit the instance of the browser
     driver.quit();
 });
 
 tap.beforeEach(function () {
-    //load the page with the driver
+    // load the page with the driver
     return driver.get(rootUrl);
 });
 
 // Function clicks the link and returns the url of the resulting page
 
-function clickFooterLinks (linkText) {
+const clickFooterLinks = function (linkText) {
     return driver.wait(webdriver.until.elementLocated(webdriver.By.id('footer')))
-    .then( function (element) {
-        return element.findElement(webdriver.By.linkText(linkText)); })
-    .then( function (element) {
-        return element.click(); })
-    .then(function () {
-        return driver.getCurrentUrl();
-    });
-}
+        .then(function (element) {
+            return element.findElement(webdriver.By.linkText(linkText));
+        })
+        .then(function (element) {
+            return element.click();
+        })
+        .then(function () {
+            return driver.getCurrentUrl();
+        });
+};
 
 // ==== ABOUT SCRATCH column ====
 
 // ABOUT SCRATCH
-tap.test('clickAboutScratchLink', options, function (t) {
-    var linkText = 'About Scratch';
-    var expectedHref = '/about';
-    clickFooterLinks(linkText).then( function (url) {
-        //the href should be at the end of the URL
+tap.test('clickAboutScratchLink', options, t => {
+    const linkText = 'About Scratch';
+    const expectedHref = '/about';
+    clickFooterLinks(linkText).then(url => {
+        // the href should be at the end of the URL
         t.equal(url.substr(-expectedHref.length), expectedHref);
         t.end();
     });
 });
 
 // FOR PARENTS
-tap.test('clickForParentsLink', options, function (t) {
-    var linkText = 'For Parents';
-    var expectedHref = '/parents/';
-    clickFooterLinks(linkText).then( function (url) {
+tap.test('clickForParentsLink', options, t => {
+    const linkText = 'For Parents';
+    const expectedHref = '/parents/';
+    clickFooterLinks(linkText).then(url => {
         t.equal(url.substr(-expectedHref.length), expectedHref);
         t.end();
     });
 });
 
 // FOR EDUCATORS
-tap.test('clickForEducatorsLink', options, function (t) {
-    var linkText = 'For Educators';
-    var expectedHref = '/educators';
-    clickFooterLinks(linkText).then( function (url) {
+tap.test('clickForEducatorsLink', options, t => {
+    const linkText = 'For Educators';
+    const expectedHref = '/educators';
+    clickFooterLinks(linkText).then(url => {
         t.equal(url.substr(-expectedHref.length), expectedHref);
         t.end();
     });
 });
 
 // FOR DEVELOPERS
-tap.test('clickForDevelopersScratchLink', options, function (t) {
-    var linkText = 'For Developers';
-    var expectedHref = '/developers';
-    clickFooterLinks(linkText).then( function (url) {
+tap.test('clickForDevelopersScratchLink', options, t => {
+    const linkText = 'For Developers';
+    const expectedHref = '/developers';
+    clickFooterLinks(linkText).then(url => {
         t.equal(url.substr(-expectedHref.length), expectedHref);
         t.end();
     });
 });
 
 // CREDITS
-tap.test('clickCreditsLink', options, function (t) {
-    var linkText = 'Credits';
-    var expectedHref = '/info/credits';
-    clickFooterLinks(linkText).then( function (url) {
+tap.test('clickCreditsLink', options, t => {
+    const linkText = 'Credits';
+    const expectedHref = '/info/credits';
+    clickFooterLinks(linkText).then(url => {
         t.equal(url.substr(-expectedHref.length), expectedHref);
         t.end();
     });
 });
 
 // JOBS
-tap.test('clickJobsLink', options, function (t) {
-    var linkText = 'Jobs';
-    var expectedHref = '/jobs';
-    clickFooterLinks(linkText).then( function (url) {
+tap.test('clickJobsLink', options, t => {
+    const linkText = 'Jobs';
+    const expectedHref = '/jobs';
+    clickFooterLinks(linkText).then(url => {
         t.equal(url.substr(-expectedHref.length), expectedHref);
         t.end();
     });
 });
 
 // PRESS
-tap.test('clickPressLink', options, function (t) {
-    var linkText = 'Press';
-    var expectedUrl = 'https://wiki.scratch.mit.edu/wiki/Scratch_Press';
-    clickFooterLinks(linkText).then( function (url) {
+tap.test('clickPressLink', options, t => {
+    const linkText = 'Press';
+    const expectedUrl = 'https://wiki.scratch.mit.edu/wiki/Scratch_Press';
+    clickFooterLinks(linkText).then(url => {
         t.equal(url, expectedUrl);
         t.end();
     });
@@ -121,40 +123,40 @@ tap.test('clickPressLink', options, function (t) {
 // ==== COMMUNITY column ====
 
 // COMMUNITY GUIDELINES
-tap.test('clickCommunityGuidelinesLink', options, function (t) {
-    var linkText = 'Community Guidelines';
-    var expectedHref = '/community_guidelines';
-    clickFooterLinks(linkText).then( function (url) {
+tap.test('clickCommunityGuidelinesLink', options, t => {
+    const linkText = 'Community Guidelines';
+    const expectedHref = '/community_guidelines';
+    clickFooterLinks(linkText).then(url => {
         t.equal(url.substr(-expectedHref.length), expectedHref);
         t.end();
     });
 });
 
 // DISCUSSION FORUMS
-tap.test('clickDiscussionForumsLink', options, function (t) {
-    var linkText = 'Discussion Forums';
-    var expectedHref = '/discuss/';
-    clickFooterLinks(linkText).then( function (url) {
+tap.test('clickDiscussionForumsLink', options, t => {
+    const linkText = 'Discussion Forums';
+    const expectedHref = '/discuss/';
+    clickFooterLinks(linkText).then(url => {
         t.equal(url.substr(-expectedHref.length), expectedHref);
         t.end();
     });
 });
 
 // SCRATCH WIKI
-tap.test('clickScratchWikiLink', options, function (t) {
-    var linkText = 'Scratch Wiki';
-    var expectedUrl = 'https://wiki.scratch.mit.edu/wiki/Scratch_Wiki_Home';
-    clickFooterLinks(linkText).then( function (url) {
+tap.test('clickScratchWikiLink', options, t => {
+    const linkText = 'Scratch Wiki';
+    const expectedUrl = 'https://wiki.scratch.mit.edu/wiki/Scratch_Wiki_Home';
+    clickFooterLinks(linkText).then(url => {
         t.equal(url, expectedUrl);
         t.end();
     });
 });
 
 // STATISTICS
-tap.test('clickStatisticsLink', options, function (t) {
-    var linkText = 'Statistics';
-    var expectedHref = '/statistics/';
-    clickFooterLinks(linkText).then( function (url) {
+tap.test('clickStatisticsLink', options, t => {
+    const linkText = 'Statistics';
+    const expectedHref = '/statistics/';
+    clickFooterLinks(linkText).then(url => {
         t.equal(url.substr(-expectedHref.length), expectedHref);
         t.end();
     });
@@ -163,60 +165,60 @@ tap.test('clickStatisticsLink', options, function (t) {
 // ==== SUPPORT column ====
 
 // TIPS PAGE
-tap.test('clickTipsPageLink', options, function (t) {
-    var linkText = 'Tips';
-    var expectedHref = '/tips';
-    clickFooterLinks(linkText).then( function (url) {
+tap.test('clickTipsPageLink', options, t => {
+    const linkText = 'Tips';
+    const expectedHref = '/tips';
+    clickFooterLinks(linkText).then(url => {
         t.equal(url.substr(-expectedHref.length), expectedHref);
         t.end();
     });
 });
 
 // FAQ
-tap.test('clickFAQLink', options, function (t) {
-    var linkText = 'FAQ';
-    var expectedHref = '/info/faq';
-    clickFooterLinks(linkText).then( function (url) {
+tap.test('clickFAQLink', options, t => {
+    const linkText = 'FAQ';
+    const expectedHref = '/info/faq';
+    clickFooterLinks(linkText).then(url => {
         t.equal(url.substr(-expectedHref.length), expectedHref);
         t.end();
     });
 });
 
 // OFFLINE EDITOR
-tap.test('clickOfflineEditorLink', options, function (t) {
-    var linkText = 'Offline Editor';
-    var expectedHref = '/download';
-    clickFooterLinks(linkText).then( function (url) {
+tap.test('clickOfflineEditorLink', options, t => {
+    const linkText = 'Offline Editor';
+    const expectedHref = '/download';
+    clickFooterLinks(linkText).then(url => {
         t.equal(url.substr(-expectedHref.length), expectedHref);
         t.end();
     });
 });
 
 // CONTACT US
-tap.test('clickContactUsLink', options, function (t) {
-    var linkText = 'Contact Us';
-    var expectedHref = '/contact-us/';
-    clickFooterLinks(linkText).then( function (url) {
+tap.test('clickContactUsLink', options, t => {
+    const linkText = 'Contact Us';
+    const expectedHref = '/contact-us/';
+    clickFooterLinks(linkText).then(url => {
         t.equal(url.substr(-expectedHref.length), expectedHref);
         t.end();
     });
 });
 
 // SCRATCH STORE
-tap.test('clickScratchStoreLink', options, function (t) {
-    var linkText = 'Scratch Store';
-    var expectedUrl = 'https://scratch-foundation.myshopify.com/';
-    clickFooterLinks(linkText).then( function (url) {
+tap.test('clickScratchStoreLink', options, t => {
+    const linkText = 'Scratch Store';
+    const expectedUrl = 'https://scratch-foundation.myshopify.com/';
+    clickFooterLinks(linkText).then(url => {
         t.equal(url, expectedUrl);
         t.end();
     });
 });
 
 // DONATE
-tap.test('clickDonateLink', options, function (t) {
-    var linkText = 'Donate';
-    var expectedUrl = 'https://secure.donationpay.org/scratchfoundation/';
-    clickFooterLinks(linkText).then( function (url) {
+tap.test('clickDonateLink', options, t => {
+    const linkText = 'Donate';
+    const expectedUrl = 'https://secure.donationpay.org/scratchfoundation/';
+    clickFooterLinks(linkText).then(url => {
         t.equal(url, expectedUrl);
         t.end();
     });
@@ -225,30 +227,30 @@ tap.test('clickDonateLink', options, function (t) {
 // ==== LEGAL column ====
 
 // TERMS OF USE
-tap.test('clickTermsOfUseLink', options, function (t) {
-    var linkText = 'Terms of Use';
-    var expectedHref = '/terms_of_use';
-    clickFooterLinks(linkText).then( function (url) {
+tap.test('clickTermsOfUseLink', options, t => {
+    const linkText = 'Terms of Use';
+    const expectedHref = '/terms_of_use';
+    clickFooterLinks(linkText).then(url => {
         t.equal(url.substr(-expectedHref.length), expectedHref);
         t.end();
     });
 });
 
 // PRIVACY POLICY
-tap.test('clickPrivacyPolicyLink', options, function (t) {
-    var linkText = 'Privacy Policy';
-    var expectedHref = '/privacy_policy';
-    clickFooterLinks(linkText).then( function (url) {
+tap.test('clickPrivacyPolicyLink', options, t => {
+    const linkText = 'Privacy Policy';
+    const expectedHref = '/privacy_policy';
+    clickFooterLinks(linkText).then(url => {
         t.equal(url.substr(-expectedHref.length), expectedHref);
         t.end();
     });
 });
 
 // DMCA
-tap.test('clickDMCALink', options, function (t) {
-    var linkText = 'DMCA';
-    var expectedHref = '/DMCA';
-    clickFooterLinks(linkText).then( function (url) {
+tap.test('clickDMCALink', options, t => {
+    const linkText = 'DMCA';
+    const expectedHref = '/DMCA';
+    clickFooterLinks(linkText).then(url => {
         t.equal(url.substr(-expectedHref.length), expectedHref);
         t.end();
     });
@@ -257,50 +259,50 @@ tap.test('clickDMCALink', options, function (t) {
 // ==== SCRATCH FAMILY column ====
 
 // SCRATCH ED (SCRATCHED)
-tap.test('clickScratchEdLink', options, function (t) {
-    var linkText = 'ScratchEd';
-    var expectedUrl = 'http://scratched.gse.harvard.edu/';
-    clickFooterLinks(linkText).then( function (url) {
+tap.test('clickScratchEdLink', options, t => {
+    const linkText = 'ScratchEd';
+    const expectedUrl = 'http://scratched.gse.harconstd.edu/';
+    clickFooterLinks(linkText).then(url => {
         t.equal(url, expectedUrl);
         t.end();
     });
 });
 
 // SCRATCH JR (SCRATCHJR)
-tap.test('clickScratchJrLink', options, function (t) {
-    var linkText = 'ScratchJr';
-    var expectedUrl = 'http://www.scratchjr.org/';
-    clickFooterLinks(linkText).then( function (url) {
+tap.test('clickScratchJrLink', options, t => {
+    const linkText = 'ScratchJr';
+    const expectedUrl = 'http://www.scratchjr.org/';
+    clickFooterLinks(linkText).then(url => {
         t.equal(url, expectedUrl);
         t.end();
     });
 });
 
 // SCRATCH DAY
-tap.test('clickScratchDayLink', options, function (t) {
-    var linkText = 'Scratch Day';
-    var expectedUrl = 'https://day.scratch.mit.edu/';
-    clickFooterLinks(linkText).then( function (url) {
+tap.test('clickScratchDayLink', options, t => {
+    const linkText = 'Scratch Day';
+    const expectedUrl = 'https://day.scratch.mit.edu/';
+    clickFooterLinks(linkText).then(url => {
         t.equal(url, expectedUrl);
         t.end();
     });
 });
 
 // SCRATCH CONFERENCE
-tap.test('clickScratchConferenceLink', options, function (t) {
-    var linkText = 'Scratch Conference';
-    var expectedHref = '/conference';
-    clickFooterLinks(linkText).then( function (url) {
+tap.test('clickScratchConferenceLink', options, t => {
+    const linkText = 'Scratch Conference';
+    const expectedHref = '/conference';
+    clickFooterLinks(linkText).then(url => {
         t.equal(url.substr(-expectedHref.length), expectedHref);
         t.end();
     });
 });
 
 // SCRATCH FOUNDATION
-tap.test('clickScratchFoundationLink', options, function (t) {
-    var linkText = 'Scratch Foundation';
-    var expectedUrl = 'https://www.scratchfoundation.org/';
-    clickFooterLinks(linkText).then( function (url) {
+tap.test('clickScratchFoundationLink', options, t => {
+    const linkText = 'Scratch Foundation';
+    const expectedUrl = 'https://www.scratchfoundation.org/';
+    clickFooterLinks(linkText).then(url => {
         t.equal(url, expectedUrl);
         t.end();
     });
diff --git a/test/integration/smoke-testing/test_navbar_links.js b/test/integration/smoke-testing/test_navbar_links.js
index 20e94ace5..efe5cf2ba 100644
--- a/test/integration/smoke-testing/test_navbar_links.js
+++ b/test/integration/smoke-testing/test_navbar_links.js
@@ -9,37 +9,39 @@ var seleniumWebdriver = require('selenium-webdriver');
 var tap = require('tap');
 
 // Selenium's promise driver will be deprecated, so we should not rely on it
-seleniumWebdriver.SELENIUM_PROMISE_MANAGER=0;
+seleniumWebdriver.SELENIUM_PROMISE_MANAGER = 0;
 
-//Set test url through environment variable
+// Set test url through environment variable
 var rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
 
-//chrome driver
-var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build();
+// chrome driver
+var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome())
+    .build();
 
-//number of tests in the plan
+// number of tests in the plan
 tap.plan(7);
 
 tap.tearDown(function () {
-    //quit the instance of the browser
+    // quit the instance of the browser
     driver.quit();
 });
 
 tap.beforeEach(function () {
-    //load the page with the driver
+    // load the page with the driver
     return driver.get(rootUrl);
 });
 
 // ==== Links in navbar ====
 
-//the create link changes depending on whether the user is signed in or not (tips window opens)
+// the create link changes depending on whether the user is signed in or not (tips window opens)
 tap.test('checkCreateLinkWhenSignedOut', function (t) {
     var xPathLink = '//li[contains(@class, "link") and contains(@class, "create")]/a';
     var expectedHref = '/projects/editor/?tip_bar=home';
     driver.findElement(seleniumWebdriver.By.xpath(xPathLink))
-        .then( function (element) {
-            return element.getAttribute('href');})
-        .then( function (url) {
+        .then(function (element) {
+            return element.getAttribute('href');
+        })
+        .then(function (url) {
             t.equal(url.substr(-expectedHref.length), expectedHref);
             t.end();
         });
@@ -49,9 +51,10 @@ tap.test('checkExploreLinkWhenSignedOut', function (t) {
     var xPathLink = '//li[contains(@class, "link") and contains(@class, "explore")]/a';
     var expectedHref = '/explore/projects/all';
     driver.findElement(seleniumWebdriver.By.xpath(xPathLink))
-        .then( function (element) {
-            return element.getAttribute('href');})
-        .then( function (url) {
+        .then(function (element) {
+            return element.getAttribute('href');
+        })
+        .then(function (url) {
             t.equal(url.substr(-expectedHref.length), expectedHref);
             t.end();
         });
@@ -61,9 +64,10 @@ tap.test('checkTipsLinkWhenSignedOut', function (t) {
     var xPathLink = '//li[contains(@class, "link") and contains(@class, "tips")]/a';
     var expectedHref = '/tips';
     driver.findElement(seleniumWebdriver.By.xpath(xPathLink))
-        .then( function (element) {
-            return element.getAttribute('href');})
-        .then( function (url) {
+        .then(function (element) {
+            return element.getAttribute('href');
+        })
+        .then(function (url) {
             t.equal(url.substr(-expectedHref.length), expectedHref);
             t.end();
         });
@@ -73,12 +77,13 @@ tap.test('checkAboutLinkWhenSignedOut', function (t) {
     var xPathLink = '//li[contains(@class, "link") and contains(@class, "about")]/a';
     var expectedHref = '/about';
     driver.findElement(seleniumWebdriver.By.xpath(xPathLink))
-    .then( function (element) {
-        return element.getAttribute('href');})
-    .then( function (url) {
-        t.equal(url.substr(-expectedHref.length), expectedHref);
-        t.end();
-    });
+        .then(function (element) {
+            return element.getAttribute('href');
+        })
+        .then(function (url) {
+            t.equal(url.substr(-expectedHref.length), expectedHref);
+            t.end();
+        });
 });
 
 // ==== Search bar ====
@@ -86,7 +91,7 @@ tap.test('checkAboutLinkWhenSignedOut', function (t) {
 tap.test('checkSearchBar', function (t) {
     var xPathLink = '//input[@id="frc-q-1088"]';
     // search bar should exist
-    driver.findElement(seleniumWebdriver.By.xpath(xPathLink)).then( function (element) {
+    driver.findElement(seleniumWebdriver.By.xpath(xPathLink)).then(function (element) {
         t.ok(element);
         t.end();
     });
@@ -98,9 +103,10 @@ tap.test('checkJoinScratchLinkWhenSignedOut', function (t) {
     var xPathLink = '//li[contains(@class, "link") and contains(@class, "right") and contains(@class, "join")]/a';
     var expectedText = 'Join Scratch';
     driver.findElement(seleniumWebdriver.By.xpath(xPathLink))
-        .then( function (element) {
-            return element.getText('a');})
-        .then( function (text) {
+        .then(function (element) {
+            return element.getText('a');
+        })
+        .then(function (text) {
             t.equal(text, expectedText);
             t.end();
         });
@@ -110,9 +116,10 @@ tap.test('checkSignInLinkWhenSignedOut', function (t) {
     var xPathLink = '//li[contains(@class, "link") and contains(@class, "right") and contains(@class, "login-item")]/a';
     var expectedText = 'Sign in';
     driver.findElement(seleniumWebdriver.By.xpath(xPathLink))
-        .then( function (element) {
-            return element.getText('a');})
-        .then( function (text) {
+        .then(function (element) {
+            return element.getText('a');
+        })
+        .then(function (text) {
             t.equal(text, expectedText);
             t.end();
         });
diff --git a/test/integration/smoke-testing/test_project_rows.js b/test/integration/smoke-testing/test_project_rows.js
index 9a2633d70..28e61bf93 100644
--- a/test/integration/smoke-testing/test_project_rows.js
+++ b/test/integration/smoke-testing/test_project_rows.js
@@ -10,85 +10,86 @@ var tap = require('tap');
 var seleniumWebdriver = require('selenium-webdriver');
 
 // Selenium's promise driver will be deprecated, so we should not rely on it
-seleniumWebdriver.SELENIUM_PROMISE_MANAGER=0;
+seleniumWebdriver.SELENIUM_PROMISE_MANAGER = 0;
 
-//chrome driver
-var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build();
+// chrome driver
+var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome())
+    .build();
 
 var rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
 
-//number of tests in the plan
+// number of tests in the plan
 tap.plan(4);
 
 tap.tearDown(function () {
-    //quit the instance of the browser
+    // quit the instance of the browser
     driver.quit();
 });
 
 tap.beforeEach(function () {
-    //load the page with the driver
+    // load the page with the driver
     return driver.get(rootUrl);
 });
 
-//checks that the title of the first row is Featured Projects
+// checks that the title of the first row is Featured Projects
 tap.test('checkFeaturedProjectsRowTitleWhenSignedOut', function (t) {
     var xPathLink = '//div[@class="box"]/div[@class="box-header"]/h4';
     driver.findElement(seleniumWebdriver.By.xpath(xPathLink))
-        .then( function (element) {
+        .then(function (element) {
             element.getText('h4')
-            .then( function (text) {
-                //expected value of the title text
-                var expectedText = 'Featured Projects';
-                t.equal(text, expectedText);
-                t.end();
-            });
+                .then(function (text) {
+                    // expected value of the title text
+                    var expectedText = 'Featured Projects';
+                    t.equal(text, expectedText);
+                    t.end();
+                });
         });
 });
 
-//checks that the link for a project makes sense
+// checks that the link for a project makes sense
 tap.test('checkFeaturedProjectsRowLinkWhenSignedOut', function (t) {
     var xPathLink = '//div[contains(@class, "thumbnail") ' +
         'and contains(@class, "project") and contains(@class, "slick-slide") ' +
         'and contains(@class, "slick-active")]/a[@class="thumbnail-image"]';
     driver.wait(seleniumWebdriver.until
         .elementLocated(seleniumWebdriver.By.xpath(xPathLink)))
-        .then( function (element) {
+        .then(function (element) {
             element.getAttribute('href')
-            .then( function (url) {
-                //expected pattern for the project URL
-                //since I don't know the length of the project ID number
-                var expectedUrlRegExp = new RegExp('/projects/.*[0-9].*/?');
-                t.match(url, expectedUrlRegExp);
-                t.end();
-            });
+                .then(function (url) {
+                    // expected pattern for the project URL
+                    // since I don't know the length of the project ID number
+                    var expectedUrlRegExp = new RegExp('/projects/.*[0-9].*/?');
+                    t.match(url, expectedUrlRegExp);
+                    t.end();
+                });
         });
 });
 
-//checks that the title of the 2nd row is Featured Studios
+// checks that the title of the 2nd row is Featured Studios
 tap.test('checkFeaturedStudiosRowWhenSignedOut', function (t) {
     var xPathLink = '//div[@class="box"][2]/div[@class="box-header"]/h4';
     driver.findElement(seleniumWebdriver.By.xpath(xPathLink))
         .then(function (element) {
             element.getText('h4')
-            .then( function (text) {
-                var expectedText = 'Featured Studios';
-                t.equal(text, expectedText);
-                t.end();
-            });
+                .then(function (text) {
+                    var expectedText = 'Featured Studios';
+                    t.equal(text, expectedText);
+                    t.end();
+                });
         });
 });
 
-//checks that the link for a studio makes sense
+// checks that the link for a studio makes sense
 tap.test('checkFeaturedStudiosRowLinkWhenSignedOut', function (t) {
     var xPathLink = '//div[contains(@class, "thumbnail") and contains(@class, "gallery") ' +
         'and contains(@class, "slick-slide") and contains(@class, "slick-active")]/a[@class="thumbnail-image"]';
     driver.findElement(seleniumWebdriver.By.xpath(xPathLink))
         .then(function (element) {
             element.getAttribute('href')
-            .then( function (url) {
-                var expectedUrlRegExp = new RegExp('/studios/.*[0-9].*/?');
-                t.match(url, expectedUrlRegExp);
-                t.end();
-            });
+                .then(function (url) {
+                    var expectedUrlRegExp = new RegExp('/studios/.*[0-9].*/?');
+                    t.match(url, expectedUrlRegExp);
+                    t.end();
+                });
         });
 });
diff --git a/test/integration/smoke-testing/test_signing_in_and_my_stuff.js b/test/integration/smoke-testing/test_signing_in_and_my_stuff.js
index f2efdd455..68bed40b5 100644
--- a/test/integration/smoke-testing/test_signing_in_and_my_stuff.js
+++ b/test/integration/smoke-testing/test_signing_in_and_my_stuff.js
@@ -42,16 +42,18 @@ tap.beforeEach(function () {
  */
 test('Trying to sign in with no username and no password using scratchr2 navbar', t => {
     clickText('Sign in')
-    .then(() => clickButton('Sign in'))
-    .then(() => driver.wait(until
-        .elementLocated(By.xpath('//form[@id="login"]/button[@type="submit"]'))))
-    .then(() => driver.wait(until
-        .elementLocated(By.xpath('//form[@id="login"]/div[@class="error"]'))))
-    .then(() => findByXpath('//form/div[@class="error"]'))
-    .then((element) => element.getText())
-    .then((text) => t.match(text, 'This field is required.',
-        '"This field is required" error should be displayed'))
-    .then(() => t.end());
+        .then(() => clickButton('Sign in'))
+        .then(() => driver.wait(until
+            .elementLocated(By.xpath('//form[@id="login"]/button[@type="submit"]')))
+        )
+        .then(() => driver.wait(until
+            .elementLocated(By.xpath('//form[@id="login"]/div[@class="error"]')))
+        )
+        .then(() => findByXpath('//form/div[@class="error"]'))
+        .then(element => element.getText())
+        .then(text => t.match(text, 'This field is required.',
+            '"This field is required" error should be displayed'))
+        .then(() => t.end());
 });
 
 /*
@@ -61,175 +63,181 @@ test('Trying to sign in with no username and no password using scratchr2 navbar'
  */
 test('Trying to sign in with no username using scratchr2 navbar', t => {
     clickText('Sign in')
-    .then(() => findByXpath('//input[@name="password"]'))
-    .then((element) => element.sendKeys(password))
-    .then(() => clickButton('Sign in'))
-    .then(() => driver.wait(until
-        .elementLocated(By.xpath('//form[@id="login"]/button[@type="submit"]'))))
-    .then(() => driver.wait(until
-        .elementLocated(By.xpath('//form[@id="login"]/div[@class="error"]'))))
-    .then(() => findByXpath('//form/div[@class="error"]'))
-    .then((element) => element.getText())
-    .then((text) => t.match(text, 'This field is required.',
-        '"This field is required" error should be displayed'))
-    .then(() => t.end());
+        .then(() => findByXpath('//input[@name="password"]'))
+        .then((element) => element.sendKeys(password))
+        .then(() => clickButton('Sign in'))
+        .then(() => driver.wait(until
+            .elementLocated(By.xpath('//form[@id="login"]/button[@type="submit"]')))
+        )
+        .then(() => driver.wait(until
+            .elementLocated(By.xpath('//form[@id="login"]/div[@class="error"]')))
+        )
+        .then(() => findByXpath('//form/div[@class="error"]'))
+        .then((element) => element.getText())
+        .then((text) => t.match(text, 'This field is required.',
+            '"This field is required" error should be displayed'))
+        .then(() => t.end());
 });
 
 test('Trying to sign in with no password using scratchr2 navbar', t => {
-    var nonsenseusername = Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5);
+    var nonsenseusername = Math.random().toString(36)
+        .replace(/[^a-z]+/g, '')
+        .substr(0, 5);
     clickText('Sign in')
-    .then(() => findByXpath('//input[@id="login_dropdown_username"]'))
-    .then((element) => element.sendKeys(nonsenseusername))
-    .then(() => clickButton('Sign in'))
-    .then(() => driver.wait(until
-        .elementLocated(By.xpath('//form[@id="login"]/button[@type="submit"]'))))
-    .then(() => driver.wait(until
-        .elementLocated(By.xpath('//form[@id="login"]/div[@class="error"]'))))
-    .then(() => findByXpath('//form/div[@class="error"]'))
-    .then((element) => element.getText())
-    .then((text) => t.match(text, 'This field is required.',
-        '"This field is required" error should be displayed'))
-    .then(() => t.end());
+        .then(() => findByXpath('//input[@id="login_dropdown_username"]'))
+        .then((element) => element.sendKeys(nonsenseusername))
+        .then(() => clickButton('Sign in'))
+        .then(() => driver.wait(until
+            .elementLocated(By.xpath('//form[@id="login"]/button[@type="submit"]'))))
+        .then(() => driver.wait(until
+            .elementLocated(By.xpath('//form[@id="login"]/div[@class="error"]'))))
+        .then(() => findByXpath('//form/div[@class="error"]'))
+        .then((element) => element.getText())
+        .then((text) => t.match(text, 'This field is required.',
+            '"This field is required" error should be displayed'))
+        .then(() => t.end());
 });
 
 test('Trying to sign in with the wrong username using scratchr2 navbar', t => {
-    var nonsenseusername = Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5);
+    var nonsenseusername = Math.random().toString(36)
+        .replace(/[^a-z]+/g, '')
+        .substr(0, 5);
     clickText('Sign in')
-    .then(() => findByXpath('//input[@id="login_dropdown_username"]'))
-    .then((element) => element.sendKeys(nonsenseusername))
-    .then(() => findByXpath('//input[@name="password"]'))
-    .then((element) => element.sendKeys(password))
-    .then(() => clickButton('Sign in'))
-    .then(() => driver.wait(until
-        .elementLocated(By.xpath('//form[@id="login"]/button[@type="submit"]'))))
-    .then(() => driver.wait(until
-        .elementLocated(By.xpath('//form[@id="login"]/div[@class="error"]'))))
-    .then(() => findByXpath('//form/div[@class="error"]'))
-    .then((element) => element.getText())
-    .then((text) => t.match(text, 'Incorrect username or password.',
-        '"Incorrect username or password" error should be displayed'))
-    .then(() => t.end());
+        .then(() => findByXpath('//input[@id="login_dropdown_username"]'))
+        .then((element) => element.sendKeys(nonsenseusername))
+        .then(() => findByXpath('//input[@name="password"]'))
+        .then((element) => element.sendKeys(password))
+        .then(() => clickButton('Sign in'))
+        .then(() => driver.wait(until
+            .elementLocated(By.xpath('//form[@id="login"]/button[@type="submit"]'))))
+        .then(() => driver.wait(until
+            .elementLocated(By.xpath('//form[@id="login"]/div[@class="error"]'))))
+        .then(() => findByXpath('//form/div[@class="error"]'))
+        .then((element) => element.getText())
+        .then((text) => t.match(text, 'Incorrect username or password.',
+            '"Incorrect username or password" error should be displayed'))
+        .then(() => t.end());
 });
 
 test('Trying to sign in with the wrong password using scratchr2 navbar', t => {
     clickText('Sign in')
-    .then(() => findByXpath('//input[@id="login_dropdown_username"]'))
-    .then((element) => element.sendKeys(username))
-    .then(() => findByXpath('//input[@name="password"]'))
-    .then((element) => element.sendKeys('nonsensepassword'))
-    .then(() => clickButton('Sign in'))
-    .then(() => driver.wait(until
-        .elementLocated(By.xpath('//form[@id="login"]/button[@type="submit"]'))))
-    .then(() => driver.wait(until
-        .elementLocated(By.xpath('//form[@id="login"]/div[@class="error"]'))))
-    .then(() => findByXpath('//form/div[@class="error"]'))
-    .then((element) => element.getText())
-    .then((text) => t.match(text, 'Incorrect username or password.',
-        '"Incorrect username or password" error should be displayed'))
-    .then(() => t.end());
+        .then(() => findByXpath('//input[@id="login_dropdown_username"]'))
+        .then((element) => element.sendKeys(username))
+        .then(() => findByXpath('//input[@name="password"]'))
+        .then((element) => element.sendKeys('nonsensepassword'))
+        .then(() => clickButton('Sign in'))
+        .then(() => driver.wait(until
+            .elementLocated(By.xpath('//form[@id="login"]/button[@type="submit"]'))))
+        .then(() => driver.wait(until
+            .elementLocated(By.xpath('//form[@id="login"]/div[@class="error"]'))))
+        .then(() => findByXpath('//form/div[@class="error"]'))
+        .then((element) => element.getText())
+        .then((text) => t.match(text, 'Incorrect username or password.',
+            '"Incorrect username or password" error should be displayed'))
+        .then(() => t.end());
 });
 
 test('Sign in to Scratch using scratchr2 navbar', t => {
     clickText('Sign in')
-    .then(() => findByXpath('//input[@id="login_dropdown_username"]'))
-    .then((element) => element.sendKeys(username))
-    .then(() => findByXpath('//input[@name="password"]'))
-    .then((element) => element.sendKeys(password))
-    .then(() => clickButton('Sign in'))
-    .then(() => findByXpath('//li[contains(@class, "logged-in-user")'
-        + 'and contains(@class, "dropdown")]/span'))
-    .then((element) => element.getText('span'))
-    .then((text) => t.match(text.toLowerCase(), username.substring(0,10).toLowerCase(),
+        .then(() => findByXpath('//input[@id="login_dropdown_username"]'))
+        .then((element) => element.sendKeys(username))
+        .then(() => findByXpath('//input[@name="password"]'))
+        .then((element) => element.sendKeys(password))
+        .then(() => clickButton('Sign in'))
+        .then(() => findByXpath('//li[contains(@class, "logged-in-user")' +
+            'and contains(@class, "dropdown")]/span'))
+        .then((element) => element.getText('span'))
+        .then((text) => t.match(text.toLowerCase(), username.substring(0, 10).toLowerCase(),
             'first part of username should be displayed in navbar'))
-    .then(() => t.end());
+        .then(() => t.end());
 });
 
 test('Sign in to Scratch & verify My Stuff structure (tabs, title)', t => {
     clickXpath('//a[@class="mystuff-icon"]')
-    .then(() => findByXpath('//div[@class="box-head"]/h2'))
-    .then((element) => element.getText('h2'))
-    .then((text) => t.equal('My Stuff', text, 'title should be My Stuff'))
-    .then(() => findByXpath('//li[@data-tab="projects"]/a'))
-    .then((element) => element.getText('a'))
-    .then((text) => t.match(text, 'All Projects', 'All Projects tab should be present'))
-    .then(() => findByXpath('//li[@data-tab="shared"]/a'))
-    .then((element) => element.getText('a'))
-    .then((text) => t.match(text, 'Shared Projects', 'Shared Projects tab should be present'))
-    .then(() => findByXpath('//li[@data-tab="unshared"]/a'))
-    .then((element) => element.getText('a'))
-    .then((text) => t.match(text, 'Unshared Projects', 'Unshared Projects tab should be present'))
-    .then(() => findByXpath('//li[@data-tab="galleries"]/a'))
-    .then((element) => element.getText('a'))
-    .then((text) => t.match(text, 'My Studios', 'My Studios tab should be present'))
-    .then(() => findByXpath('//li[@data-tab="trash"]/a'))
-    .then((element) => element.getText('a'))
-    .then((text) => t.match(text, 'Trash', 'Trash tab should be present'))
-    .then(() => t.end());
+        .then(() => findByXpath('//div[@class="box-head"]/h2'))
+        .then((element) => element.getText('h2'))
+        .then((text) => t.equal('My Stuff', text, 'title should be My Stuff'))
+        .then(() => findByXpath('//li[@data-tab="projects"]/a'))
+        .then((element) => element.getText('a'))
+        .then((text) => t.match(text, 'All Projects', 'All Projects tab should be present'))
+        .then(() => findByXpath('//li[@data-tab="shared"]/a'))
+        .then((element) => element.getText('a'))
+        .then((text) => t.match(text, 'Shared Projects', 'Shared Projects tab should be present'))
+        .then(() => findByXpath('//li[@data-tab="unshared"]/a'))
+        .then((element) => element.getText('a'))
+        .then((text) => t.match(text, 'Unshared Projects', 'Unshared Projects tab should be present'))
+        .then(() => findByXpath('//li[@data-tab="galleries"]/a'))
+        .then((element) => element.getText('a'))
+        .then((text) => t.match(text, 'My Studios', 'My Studios tab should be present'))
+        .then(() => findByXpath('//li[@data-tab="trash"]/a'))
+        .then((element) => element.getText('a'))
+        .then((text) => t.match(text, 'Trash', 'Trash tab should be present'))
+        .then(() => t.end());
 });
 
 test('clicking See Inside should take you to the editor', t => {
     clickXpath('//a[@class="mystuff-icon"]')
-    .then(() => findByXpath('//a[@data-control="edit"]'))
-    .then((element) => element.getText('span'))
-    .then((text) => t.equal(text, 'See inside', 'there should be a "See inside" button'))
-    .then(() => clickXpath('//a[@data-control="edit"]'))
-    .then(() => driver.getCurrentUrl())
-    .then( function (url) {
-        var expectedUrl = '/#editor';
-        t.equal(url.substr(-expectedUrl.length), expectedUrl, 'after clicking, the URL should end in #editor');
-    })
-    .then(() => t.end());
+        .then(() => findByXpath('//a[@data-control="edit"]'))
+        .then((element) => element.getText('span'))
+        .then((text) => t.equal(text, 'See inside', 'there should be a "See inside" button'))
+        .then(() => clickXpath('//a[@data-control="edit"]'))
+        .then(() => driver.getCurrentUrl())
+        .then(function (u) {
+            var expectedUrl = '/#editor';
+            t.equal(u.substr(-expectedUrl.length), expectedUrl, 'after clicking, the URL should end in #editor');
+        })
+        .then(() => t.end());
 });
 
 test('clicking a project title should take you to the project page', t => {
     clickXpath('//a[@class="mystuff-icon"]')
-    .then(() => clickXpath('//a[@data-control="edit"]'))
-    .then(() => driver.getCurrentUrl())
-    .then( function (url) {
-        var expectedUrlRegExp = new RegExp('/projects/.*[0-9].*/?');
-        t.match(url, expectedUrlRegExp, 'after clicking, the URL should end in projects/PROJECT_ID/');
-    })
-    .then(() => t.end());
+        .then(() => clickXpath('//a[@data-control="edit"]'))
+        .then(() => driver.getCurrentUrl())
+        .then(function (u) {
+            var expectedUrlRegExp = new RegExp('/projects/.*[0-9].*/?');
+            t.match(u, expectedUrlRegExp, 'after clicking, the URL should end in projects/PROJECT_ID/');
+        })
+        .then(() => t.end());
 });
 
 test('Add To button should bring up a list of studios', t => {
     clickXpath('//a[@class="mystuff-icon"]')
-    .then(() => findByXpath('//div[@data-control="add-to"]'))
-    .then((element) => element.getText('span'))
-    .then((text) => t.equal(text, 'Add to', 'there should be an "Add to" button'))
-    .then(() => clickXpath('//div[@data-control="add-to"]'))
-    .then(() => findByXpath('//div[@class="dropdown-menu"]/ul/li'))
-    .then((element) => element.getText('span'))
-    .then( function (text) {
-        var expectedRegExp = new RegExp('.+');
-        t.match(text, expectedRegExp, 'the dropdown menu should have at least 1 text item in it');
-    })
-    .then(() => t.end());
+        .then(() => findByXpath('//div[@data-control="add-to"]'))
+        .then((element) => element.getText('span'))
+        .then((text) => t.equal(text, 'Add to', 'there should be an "Add to" button'))
+        .then(() => clickXpath('//div[@data-control="add-to"]'))
+        .then(() => findByXpath('//div[@class="dropdown-menu"]/ul/li'))
+        .then((element) => element.getText('span'))
+        .then(function (text) {
+            var expectedRegExp = new RegExp('.+');
+            t.match(text, expectedRegExp, 'the dropdown menu should have at least 1 text item in it');
+        })
+        .then(() => t.end());
 });
 
 test('+ New Studio button should take you to the studio page', t => {
     clickXpath('//a[@class="mystuff-icon"]')
-    .then(() => clickXpath('//form[@id="new_studio"]/button[@type="submit"]'))
-    .then(() => findByXpath('//div[@id="show-add-project"]'))
-    .then((element) => element.getText('span'))
-    .then((text) => t.equal(text, 'Add projects', 'there should be an "Add projects" button'))
-    .then(() => driver.getCurrentUrl())
-    .then( function (url) {
-        var expectedUrlRegExp = new RegExp('/studios/.*[0-9].*/?');
-        t.match(url, expectedUrlRegExp,
-            'after clicking the + New Studio, the URL should end in studios/STUDIO_ID');
-    })
-    .then(() => t.end());
+        .then(() => clickXpath('//form[@id="new_studio"]/button[@type="submit"]'))
+        .then(() => findByXpath('//div[@id="show-add-project"]'))
+        .then((element) => element.getText('span'))
+        .then((text) => t.equal(text, 'Add projects', 'there should be an "Add projects" button'))
+        .then(() => driver.getCurrentUrl())
+        .then(function (u) {
+            var expectedUrlRegExp = new RegExp('/studios/.*[0-9].*/?');
+            t.match(u, expectedUrlRegExp,
+                'after clicking the + New Studio, the URL should end in studios/STUDIO_ID');
+        })
+        .then(() => t.end());
 });
 
 test('+ New Project button should open the editor', t => {
     clickXpath('//a[@class="mystuff-icon"]')
-    .then(() => clickText('+ New Project'))
-    .then(() => driver.getCurrentUrl())
-    .then( function (url) {
-        var expectedUrlRegExp = new RegExp('/projects/editor');
-        t.match(url, expectedUrlRegExp,
-            'after clicking, the URL should end in projects/editor');
-    })
-    .then(() => t.end());
+        .then(() => clickText('+ New Project'))
+        .then(() => driver.getCurrentUrl())
+        .then(function (u) {
+            var expectedUrlRegExp = new RegExp('/projects/editor');
+            t.match(u, expectedUrlRegExp,
+                'after clicking, the URL should end in projects/editor');
+        })
+        .then(() => t.end());
 });
diff --git a/test/integration/smoke-testing/test_signing_in_and_out_discuss.js b/test/integration/smoke-testing/test_signing_in_and_out_discuss.js
index 65d48c64b..030c12040 100644
--- a/test/integration/smoke-testing/test_signing_in_and_out_discuss.js
+++ b/test/integration/smoke-testing/test_signing_in_and_out_discuss.js
@@ -36,24 +36,24 @@ tap.beforeEach(function () {
 
 test('Sign in to Scratch using scratchr2 navbar', t => {
     clickText('Sign in')
-    .then(() => findByXpath('//input[@id="login_dropdown_username"]'))
-    .then((element) => element.sendKeys(username))
-    .then(() => findByXpath('//input[@name="password"]'))
-    .then((element) => element.sendKeys(password))
-    .then(() => clickButton('Sign in'))
-    .then(() => findByXpath('//li[contains(@class, "logged-in-user")'
-        + 'and contains(@class, "dropdown")]/span'))
-    .then((element) => element.getText('span'))
-    .then((text) => t.match(text.toLowerCase(), username.substring(0,10).toLowerCase(),
+        .then(() => findByXpath('//input[@id="login_dropdown_username"]'))
+        .then((element) => element.sendKeys(username))
+        .then(() => findByXpath('//input[@name="password"]'))
+        .then((element) => element.sendKeys(password))
+        .then(() => clickButton('Sign in'))
+        .then(() => findByXpath('//li[contains(@class, "logged-in-user")' +
+        'and contains(@class, "dropdown")]/span'))
+        .then((element) => element.getText('span'))
+        .then((text) => t.match(text.toLowerCase(), username.substring(0, 10).toLowerCase(),
             'first part of username should be displayed in navbar'))
-    .then(() => t.end());
+        .then(() => t.end());
 });
 
 test('Sign out of Scratch using scratchr2 navbar', t => {
-    clickXpath('//span[contains(@class, "user-name")'
-        + ' and contains(@class, "dropdown-toggle")]/img[@class="user-icon"]')
-    .then(() => clickXpath('//input[@value="Sign out"]'))
-    .then(() => findText('Sign in'))
-    .then((element) => t.ok(element, 'Sign in reappeared on the page after signing out'))
-    .then(() => t.end());
+    clickXpath('//span[contains(@class, "user-name")' +
+        ' and contains(@class, "dropdown-toggle")]/img[@class="user-icon"]')
+        .then(() => clickXpath('//input[@value="Sign out"]'))
+        .then(() => findText('Sign in'))
+        .then((element) => t.ok(element, 'Sign in reappeared on the page after signing out'))
+        .then(() => t.end());
 });
diff --git a/test/integration/smoke-testing/test_signing_in_and_out_homepage.js b/test/integration/smoke-testing/test_signing_in_and_out_homepage.js
index 43775dee2..29bdf5dae 100644
--- a/test/integration/smoke-testing/test_signing_in_and_out_homepage.js
+++ b/test/integration/smoke-testing/test_signing_in_and_out_homepage.js
@@ -33,26 +33,23 @@ tap.beforeEach(function () {
 
 test('Sign in to Scratch using scratch-www navbar', t => {
     clickText('Sign in')
-    .then(() => findByXpath('//input[@id="frc-username-1088"]'))
-    .then((element) => element.sendKeys(username))
-    .then(() => findByXpath('//input[@id="frc-password-1088"]'))
-    .then((element) => element.sendKeys(password))
-    .then(() => clickXpath('//button[contains(@class, "button") and '
-        + 'contains(@class, "submit-button") and contains(@class, "white")]'))
-    .then(() => findByXpath('//span[@class="profile-name"]'))
-    .then((element) => element.getText())
-    .then((text) => t.match(text.toLowerCase(), username.substring(0,10).toLowerCase(),
+        .then(() => findByXpath('//input[@id="frc-username-1088"]'))
+        .then((element) => element.sendKeys(username))
+        .then(() => findByXpath('//input[@id="frc-password-1088"]'))
+        .then((element) => element.sendKeys(password))
+        .then(() => clickXpath('//button[contains(@class, "button") and ' +
+            'contains(@class, "submit-button") and contains(@class, "white")]'))
+        .then(() => findByXpath('//span[@class="profile-name"]'))
+        .then((element) => element.getText())
+        .then((text) => t.match(text.toLowerCase(), username.substring(0, 10).toLowerCase(),
             'first part of username should be displayed in navbar'))
-    .then(() => t.end());
+        .then(() => t.end());
 });
 
 test('Sign out of Scratch using scratch-www navbar', t => {
     clickXpath('//a[@class="user-info"]')
-    .then(() => clickText('Sign out'))
-    .then(() => findText('Sign in'))
-    .then((element) => t.ok(element, 'Sign in reappeared on the page after signing out'))
-    .then(() => t.end());
+        .then(() => clickText('Sign out'))
+        .then(() => findText('Sign in'))
+        .then((element) => t.ok(element, 'Sign in reappeared on the page after signing out'))
+        .then(() => t.end());
 });
-
-
-
diff --git a/test/integration/smoke-testing/test_statistics_page.js b/test/integration/smoke-testing/test_statistics_page.js
index 1d636e352..15c044e27 100644
--- a/test/integration/smoke-testing/test_statistics_page.js
+++ b/test/integration/smoke-testing/test_statistics_page.js
@@ -37,24 +37,28 @@ tap.beforeEach(function () {
 test('check that Monthly Activity Trends title is present & correct', t => {
     var chartTitle = 'Monthly Activity Trends';
     findByCss('div.box-head h3')
-    .then((element) => element.getText('h3'))
-    .then((text) => t.equal(text, chartTitle, 'chart title should be Monthly Activity Trends'))
-    .then(() => t.end());
+        .then((element) => element.getText('h3'))
+        .then((text) => t.equal(text, chartTitle, 'chart title should be Monthly Activity Trends'))
+        .then(() => t.end());
 });
 
 test('check that Monthly Activity Trends chart > New Projects label is toggleable', t => {
-    var classXpath = `(//div[@id="activity_chart"]/*[name()='svg']/*[name()='g']/*[name()='g']/*`
-        + `[name()='g'])[4]/*[name()='g']/*[name()='g']/*[name()='g']`;
+    var classXpath = `(//div[@id="activity_chart"]/*[name()='svg']/*[name()='g']/*[name()='g']/*` +
+        `[name()='g'])[4]/*[name()='g']/*[name()='g']/*[name()='g']`;
     findByXpath(classXpath)
-    .then((element) => element.getAttribute('class'))
-    .then((classtext) => t.equal(classtext, 'nv-series', 'by default, New Projects should be enabled'))
-    .then(() => clickText('New Projects'))
-    .then(() => findByXpath(classXpath))
-    .then((element) => element.getAttribute('class'))
-    .then((classtext) => t.equal(classtext, 'nv-series nv-disabled', 'when clicked, New Projects should be disabled'))
-    .then(() => clickText('New Projects'))
-    .then(() => findByXpath(classXpath))
-    .then((element) => element.getAttribute('class'))
-    .then((classtext) => t.equal(classtext, 'nv-series', 'when clicked again, New Projects should be enabled'))
-    .then(() => t.end());
+        .then((element) => element.getAttribute('class'))
+        .then((classtext) => t.equal(classtext, 'nv-series', 'by default, New Projects should be enabled'))
+        .then(() => clickText('New Projects'))
+        .then(() => findByXpath(classXpath))
+        .then((element) => element.getAttribute('class'))
+        .then((classtext) => t.equal(
+            classtext,
+            'nv-series nv-disabled',
+            'when clicked, New Projects should be disabled'
+        ))
+        .then(() => clickText('New Projects'))
+        .then(() => findByXpath(classXpath))
+        .then((element) => element.getAttribute('class'))
+        .then((classtext) => t.equal(classtext, 'nv-series', 'when clicked again, New Projects should be enabled'))
+        .then(() => t.end());
 });
diff --git a/test/integration/teacher-registration/teacher_registration_utils.js b/test/integration/teacher-registration/teacher_registration_utils.js
index 424ca4319..dfc828f68 100644
--- a/test/integration/teacher-registration/teacher_registration_utils.js
+++ b/test/integration/teacher-registration/teacher_registration_utils.js
@@ -1,11 +1,11 @@
 module.exports.constants = {
-    'nextStepXpath': '//button[span[contains(text(), "Next Step")]]',
-    'generalErrorMessageXpath': '//span[@class="help-block validation-message"]/span[contains(text(),'
-        + '"This field is required")]',
-    'loremIpsumTextLong': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur viverra'
-     + 'nec mauris efficitur tincidunt. Vestibulum ut diam odio. Cum sociis natoque penatibus et magnis dis'
-     + 'parturient montes, nascetur ridiculus mus. Morbi non enim dolor. Vestibulum at enim vestibulum, ullamcorper'
-     + 'Duis eget quam pharetra, ultricies est eu, pharetra nisi. In tempor cursus nisi, non sagittis quam gravida.'
+    nextStepXpath: '//button[span[contains(text(), "Next Step")]]',
+    generalErrorMessageXpath: '//span[@class="help-block validation-message"]/span[contains(text(),' +
+        '"This field is required")]',
+    loremIpsumTextLong: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur viverra' +
+     'nec mauris efficitur tincidunt. Vestibulum ut diam odio. Cum sociis natoque penatibus et magnis dis' +
+     'parturient montes, nascetur ridiculus mus. Morbi non enim dolor. Vestibulum at enim vestibulum, ullamcorper' +
+     'Duis eget quam pharetra, ultricies est eu, pharetra nisi. In tempor cursus nisi, non sagittis quam gravida.'
 };
 
 module.exports.fillUsernameSlide = function (driver, seleniumWebdriver) {
@@ -14,7 +14,7 @@ module.exports.fillUsernameSlide = function (driver, seleniumWebdriver) {
     var usernamePromise = usernameInput.sendKeys('clipspringer');
     var passwordPromise = passwordInput.sendKeys('educators');
     var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(module.exports.constants.nextStepXpath));
-    return Promise.all([usernamePromise, passwordPromise]).then(function () {
+    return Promise.all([usernamePromise, passwordPromise]).then(function () { // eslint-disable-line no-undef
         nextStepButton.click().then(function () {
             driver.wait(seleniumWebdriver.until
                 .elementLocated(seleniumWebdriver.By.className('demographics-step')));
@@ -28,7 +28,7 @@ module.exports.fillDemographicsSlide = function (driver, seleniumWebdriver) {
     var selectCountry = driver.findElement(seleniumWebdriver.By.xpath('//select[@name="user.country"]' +
         '/option[@value="us"]')).click();
     var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(module.exports.constants.nextStepXpath));
-    return Promise.all([clickMaleInput, selectCountry]).then(function () {
+    return Promise.all([clickMaleInput, selectCountry]).then(function () { // eslint-disable-line no-undef
         nextStepButton.click().then(function () {
             driver.wait(seleniumWebdriver.until
                 .elementLocated(seleniumWebdriver.By.className('name-step')));
@@ -40,7 +40,7 @@ module.exports.fillNameSlide = function (driver, seleniumWebdriver) {
     var firstNamePromise = driver.findElement(seleniumWebdriver.By.name('user.name.first')).sendKeys('first');
     var lastNamePromise = driver.findElement(seleniumWebdriver.By.name('user.name.last')).sendKeys('surname');
     var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(module.exports.constants.nextStepXpath));
-    return Promise.all([firstNamePromise, lastNamePromise]).then(function () {
+    return Promise.all([firstNamePromise, lastNamePromise]).then(function () { // eslint-disable-line no-undef
         nextStepButton.click().then(function () {
             driver.wait(seleniumWebdriver.until
                 .elementLocated(seleniumWebdriver.By.className('phone-step')));
@@ -53,8 +53,8 @@ module.exports.fillPhoneSlide = function (driver, seleniumWebdriver) {
     var consentCheckbox = driver.findElement(seleniumWebdriver.By.name('phoneConsent'));
     var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(module.exports.constants.nextStepXpath));
     var phoneNumberPromise = phoneInput.sendKeys('6172535960');
-    var consentPromise =  consentCheckbox.click();
-    return Promise.all([phoneNumberPromise, consentPromise]).then(function () {
+    var consentPromise = consentCheckbox.click();
+    return Promise.all([phoneNumberPromise, consentPromise]).then(function () { // eslint-disable-line no-undef
         nextStepButton.click().then(function () {
             driver.wait(seleniumWebdriver.until
                 .elementLocated(seleniumWebdriver.By.className('organization-step')));
@@ -68,14 +68,15 @@ module.exports.fillOrganizationSlide = function (driver, seleniumWebdriver) {
     var typeCheckbox = driver.findElement(seleniumWebdriver.By.xpath('//input[@type="checkbox" and @value="3"]'));
     var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(module.exports.constants.nextStepXpath));
     var organizationPromise = organizationInput.sendKeys('MIT Media Lab');
-    var titlePromise =  titleInput.sendKeys('Software Developer');
+    var titlePromise = titleInput.sendKeys('Software Developer');
     var typePromise = typeCheckbox.click();
-    return Promise.all([organizationPromise, titlePromise, typePromise]).then(function () {
-        nextStepButton.click().then(function () {
-            driver.wait(seleniumWebdriver.until
-                .elementLocated(seleniumWebdriver.By.className('address-step')));
+    return Promise.all([organizationPromise, titlePromise, typePromise]) // eslint-disable-line no-undef
+        .then(function () {
+            nextStepButton.click().then(function () {
+                driver.wait(seleniumWebdriver.until
+                    .elementLocated(seleniumWebdriver.By.className('address-step')));
+            });
         });
-    });
 };
 
 module.exports.fillAddressSlide = function (driver, seleniumWebdriver) {
@@ -88,10 +89,11 @@ module.exports.fillAddressSlide = function (driver, seleniumWebdriver) {
     var statePromise = driver.findElement(seleniumWebdriver.By.xpath('//select[@name="address.state"]' +
         '/option[@value="us-ma"]')).click();
     var zipPromise = zipCodeInput.sendKeys('02139');
-    return Promise.all([addressPromise, cityPromise, statePromise, zipPromise]).then(function () {
-        nextStepButton.click().then(function () {
-            driver.wait(seleniumWebdriver.until
-                .elementLocated(seleniumWebdriver.By.className('usescratch-step')));
+    return Promise.all([addressPromise, cityPromise, statePromise, zipPromise]) // eslint-disable-line no-undef
+        .then(function () {
+            nextStepButton.click().then(function () {
+                driver.wait(seleniumWebdriver.until
+                    .elementLocated(seleniumWebdriver.By.className('usescratch-step')));
+            });
         });
-    });
 };
diff --git a/test/integration/teacher-registration/test_teacher_registration_address_step.js b/test/integration/teacher-registration/test_teacher_registration_address_step.js
index 2a0188422..557f5155f 100644
--- a/test/integration/teacher-registration/test_teacher_registration_address_step.js
+++ b/test/integration/teacher-registration/test_teacher_registration_address_step.js
@@ -10,11 +10,12 @@ var tap = require('tap');
 var utils = require('./teacher_registration_utils.js');
 var constants = utils.constants;
 
-//Set test url through environment variable
+// Set test url through environment variable
 var rootUrl = process.env.ROOT_URL || 'http://localhost:8333';
 
-//chrome driver
-var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build();
+// chrome driver
+var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome())
+    .build();
 
 tap.plan(2);
 
@@ -25,16 +26,16 @@ tap.tearDown(function () {
 tap.beforeEach(function () {
     driver.get(rootUrl + '/educators/register');
     return utils.fillUsernameSlide(driver, seleniumWebdriver)
-        .then(utils.fillDemographicsSlide.bind(this, driver, seleniumWebdriver))
-        .then(utils.fillNameSlide.bind(this, driver, seleniumWebdriver))
-        .then(utils.fillPhoneSlide.bind(this, driver, seleniumWebdriver))
-        .then(utils.fillOrganizationSlide.bind(this, driver, seleniumWebdriver));
+        .then(utils.fillDemographicsSlide.bind(this, driver, seleniumWebdriver)) // eslint-disable-line no-invalid-this
+        .then(utils.fillNameSlide.bind(this, driver, seleniumWebdriver)) // eslint-disable-line no-invalid-this
+        .then(utils.fillPhoneSlide.bind(this, driver, seleniumWebdriver)) // eslint-disable-line no-invalid-this
+        .then(utils.fillOrganizationSlide.bind(this, driver, seleniumWebdriver)); // eslint-disable-line no-invalid-this
 });
 
-//Selects Vatican City as the country, and checks that the state dropdown disappears
+// Selects Vatican City as the country, and checks that the state dropdown disappears
 tap.test('checkStateDropdownOnlyPresentWhenNeeded', function (t) {
     driver.findElement(seleniumWebdriver.By.xpath('//select[@name="address.country"]' +
-        '/option[@value="va"]')).click() //select Vatican City as the country
+        '/option[@value="va"]')).click() // select Vatican City as the country
         .then(function () {
             driver.findElements(seleniumWebdriver.By.name('address.state'))
                 .then(function (stateDropdown) {
@@ -46,9 +47,9 @@ tap.test('checkStateDropdownOnlyPresentWhenNeeded', function (t) {
 
 tap.test('checkZipCodeRequired', function (t) {
     var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath));
-    var errorMessageXPath = '//input[@name="address.zip"]/following-sibling::'
-        + 'span[@class="help-block validation-message"]/span[contains(text(),'
-        + '"This field is required")]';
+    var errorMessageXPath = '//input[@name="address.zip"]/following-sibling::' +
+        'span[@class="help-block validation-message"]/span[contains(text(),' +
+        '"This field is required")]';
     nextStepButton.click().then(function () {
         driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath))
             .then(function (validationMessages) {
@@ -57,4 +58,3 @@ tap.test('checkZipCodeRequired', function (t) {
             });
     });
 });
-
diff --git a/test/integration/teacher-registration/test_teacher_registration_demographics_step.js b/test/integration/teacher-registration/test_teacher_registration_demographics_step.js
index ee176a9fb..cb589d137 100644
--- a/test/integration/teacher-registration/test_teacher_registration_demographics_step.js
+++ b/test/integration/teacher-registration/test_teacher_registration_demographics_step.js
@@ -10,11 +10,12 @@ var tap = require('tap');
 var utils = require('./teacher_registration_utils.js');
 var constants = utils.constants;
 
-//Set test url through environment variable
+// Set test url through environment variable
 var rootUrl = process.env.ROOT_URL || 'http://localhost:8333';
 
-//chrome driver
-var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build();
+// chrome driver
+var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome())
+    .build();
 
 tap.plan(2);
 
@@ -27,8 +28,8 @@ tap.beforeEach(function () {
     return utils.fillUsernameSlide(driver, seleniumWebdriver);
 });
 
-//if the user selects the other gender option, they must input a gender
-//selects the other gender option and attempt to advance the slide
+// if the user selects the other gender option, they must input a gender
+// selects the other gender option and attempt to advance the slide
 tap.test('checkOtherGenderInput', function (t) {
     var otherGenderRadio = driver.findElement(seleniumWebdriver.By.xpath('//input[@value="other"' +
         'and @type="radio"]'));
@@ -37,25 +38,24 @@ tap.test('checkOtherGenderInput', function (t) {
     otherGenderRadio.click().then(function () {
         nextStepButton.click().then(function () {
             driver.findElements(seleniumWebdriver.By.xpath(constants.generalErrorMessageXpath))
-            .then(function (validationMessages) {
-                t.equal(validationMessages.length, 1);
-                t.end();
-            });
+                .then(function (validationMessages) {
+                    t.equal(validationMessages.length, 1);
+                    t.end();
+                });
         });
     });
 });
 
-//the user must select a gender
-//tries to advance the slide without selecting a gender
+// the user must select a gender
+// tries to advance the slide without selecting a gender
 tap.test('checkNoGenderInput', function (t) {
     var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath));
     driver.findElement(seleniumWebdriver.By.xpath('//select[@name="user.country"]/option[2]')).click();
     nextStepButton.click().then(function () {
-            driver.findElements(seleniumWebdriver.By.xpath(constants.generalErrorMessageXpath))
+        driver.findElements(seleniumWebdriver.By.xpath(constants.generalErrorMessageXpath))
             .then(function (validationMessages) {
                 t.equal(validationMessages.length, 1);
                 t.end();
             });
-        });
+    });
 });
-
diff --git a/test/integration/teacher-registration/test_teacher_registration_name_step.js b/test/integration/teacher-registration/test_teacher_registration_name_step.js
index b30bc5ffe..dee76448d 100644
--- a/test/integration/teacher-registration/test_teacher_registration_name_step.js
+++ b/test/integration/teacher-registration/test_teacher_registration_name_step.js
@@ -10,11 +10,12 @@ var tap = require('tap');
 var utils = require('./teacher_registration_utils.js');
 var constants = utils.constants;
 
-//Set test url through environment variable
+// Set test url through environment variable
 var rootUrl = process.env.ROOT_URL || 'http://localhost:8333';
 
-//chrome driver
-var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build();
+// chrome driver
+var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome())
+    .build();
 
 tap.plan(2);
 
@@ -25,15 +26,15 @@ tap.tearDown(function () {
 tap.beforeEach(function () {
     driver.get(rootUrl + '/educators/register');
     return utils.fillUsernameSlide(driver, seleniumWebdriver)
-        .then(utils.fillDemographicsSlide.bind(this, driver, seleniumWebdriver));
+        .then(utils.fillDemographicsSlide.bind(this, driver, seleniumWebdriver)); // eslint-disable-line no-invalid-this
 });
 
-//attempts to advance the slide without inputting either name, checks that both give the correct error
+// attempts to advance the slide without inputting either name, checks that both give the correct error
 tap.test('checkFirstNameRequired', function (t) {
     var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath));
-    var errorMessageXPath = '//input[@name="user.name.first"]/following-sibling::'
-        + 'span[@class="help-block validation-message"]/span[contains(text(),'
-        + '"This field is required")]';
+    var errorMessageXPath = '//input[@name="user.name.first"]/following-sibling::' +
+        'span[@class="help-block validation-message"]/span[contains(text(),' +
+        '"This field is required")]';
     nextStepButton.click().then(function () {
         driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath))
             .then(function (validationMessages) {
@@ -43,12 +44,12 @@ tap.test('checkFirstNameRequired', function (t) {
     });
 });
 
-//attempts to advance the slide without inputting either name, checks that both give the correct error
+// attempts to advance the slide without inputting either name, checks that both give the correct error
 tap.test('checkLastNameRequired', function (t) {
     var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath));
-    var errorMessageXPath = '//input[@name="user.name.last"]/following-sibling::'
-        + 'span[@class="help-block validation-message"]/span[contains(text(),'
-        + '"This field is required")]';
+    var errorMessageXPath = '//input[@name="user.name.last"]/following-sibling::' +
+        'span[@class="help-block validation-message"]/span[contains(text(),' +
+        '"This field is required")]';
     nextStepButton.click().then(function () {
         driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath))
             .then(function (validationMessages) {
@@ -57,4 +58,3 @@ tap.test('checkLastNameRequired', function (t) {
             });
     });
 });
-
diff --git a/test/integration/teacher-registration/test_teacher_registration_organization_step.js b/test/integration/teacher-registration/test_teacher_registration_organization_step.js
index ba98e5baa..6a136ecea 100644
--- a/test/integration/teacher-registration/test_teacher_registration_organization_step.js
+++ b/test/integration/teacher-registration/test_teacher_registration_organization_step.js
@@ -10,11 +10,12 @@ var tap = require('tap');
 var utils = require('./teacher_registration_utils.js');
 var constants = utils.constants;
 
-//Set test url through environment variable
+// Set test url through environment variable
 var rootUrl = process.env.ROOT_URL || 'http://localhost:8333';
 
-//chrome driver
-var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build();
+// chrome driver
+var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome())
+    .build();
 
 tap.plan(4);
 
@@ -25,9 +26,9 @@ tap.tearDown(function () {
 tap.beforeEach(function () {
     driver.get(rootUrl + '/educators/register');
     return utils.fillUsernameSlide(driver, seleniumWebdriver)
-        .then(utils.fillDemographicsSlide.bind(this, driver, seleniumWebdriver))
-        .then(utils.fillNameSlide.bind(this, driver, seleniumWebdriver))
-        .then(utils.fillPhoneSlide.bind(this, driver, seleniumWebdriver));
+        .then(utils.fillDemographicsSlide.bind(this, driver, seleniumWebdriver)) // eslint-disable-line no-invalid-this
+        .then(utils.fillNameSlide.bind(this, driver, seleniumWebdriver)) // eslint-disable-line no-invalid-this
+        .then(utils.fillPhoneSlide.bind(this, driver, seleniumWebdriver)); // eslint-disable-line no-invalid-this
 });
 
 tap.test('otherFieldRequiredIfChecked', function (t) {
@@ -47,9 +48,9 @@ tap.test('otherFieldRequiredIfChecked', function (t) {
 
 tap.test('checkOrganizationFieldRequired', function (t) {
     var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath));
-    var errorMessageXPath = '//input[@name="organization.name"]/following-sibling::'
-        + 'span[@class="help-block validation-message"]/span[contains(text(),'
-        + '"This field is required")]';
+    var errorMessageXPath = '//input[@name="organization.name"]/following-sibling::' +
+        'span[@class="help-block validation-message"]/span[contains(text(),' +
+        '"This field is required")]';
     nextStepButton.click().then(function () {
         driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath))
             .then(function (validationMessages) {
@@ -61,9 +62,9 @@ tap.test('checkOrganizationFieldRequired', function (t) {
 
 tap.test('checkRoleFieldRequired', function (t) {
     var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath));
-    var errorMessageXPath = '//input[@name="organization.title"]/following-sibling::'
-        + 'span[@class="help-block validation-message"]/span[contains(text(),'
-        + '"This field is required")]';
+    var errorMessageXPath = '//input[@name="organization.title"]/following-sibling::' +
+        'span[@class="help-block validation-message"]/span[contains(text(),' +
+        '"This field is required")]';
     nextStepButton.click().then(function () {
         driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath))
             .then(function (validationMessages) {
@@ -75,9 +76,9 @@ tap.test('checkRoleFieldRequired', function (t) {
 
 tap.test('checkOrganizationTypeRequired', function (t) {
     var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath));
-    var errorMessageXPath = '//div[@class="checkbox"]/following-sibling::'
-        + 'span[@class="help-block validation-message" and contains(text(),'
-        + '"This field is required")]';
+    var errorMessageXPath = '//div[@class="checkbox"]/following-sibling::' +
+        'span[@class="help-block validation-message" and contains(text(),' +
+        '"This field is required")]';
     nextStepButton.click().then(function () {
         driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath))
             .then(function (validationMessages) {
diff --git a/test/integration/teacher-registration/test_teacher_registration_phone_step.js b/test/integration/teacher-registration/test_teacher_registration_phone_step.js
index 2456567ac..ec3feaac9 100644
--- a/test/integration/teacher-registration/test_teacher_registration_phone_step.js
+++ b/test/integration/teacher-registration/test_teacher_registration_phone_step.js
@@ -9,11 +9,12 @@ var tap = require('tap');
 
 var utils = require('./teacher_registration_utils.js');
 
-//Set test url through environment variable
+// Set test url through environment variable
 var rootUrl = process.env.ROOT_URL || 'http://localhost:8333';
 
-//chrome driver
-var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build();
+// chrome driver
+var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome())
+    .build();
 
 tap.plan(1);
 
@@ -24,16 +25,16 @@ tap.tearDown(function () {
 tap.beforeEach(function () {
     driver.get(rootUrl + '/educators/register');
     return utils.fillUsernameSlide(driver, seleniumWebdriver)
-        .then(utils.fillDemographicsSlide.bind(this, driver, seleniumWebdriver))
-        .then(utils.fillNameSlide.bind(this, driver, seleniumWebdriver));
+        .then(utils.fillDemographicsSlide.bind(this, driver, seleniumWebdriver)) // eslint-disable-line no-invalid-this
+        .then(utils.fillNameSlide.bind(this, driver, seleniumWebdriver)); // eslint-disable-line no-invalid-this
 });
 
-//inputs an invalid phone number and checks that the correct error message appears
+// inputs an invalid phone number and checks that the correct error message appears
 tap.test('validatePhoneNumber', function (t) {
     var phoneInput = driver.findElement(seleniumWebdriver.By.xpath('//input[@type="tel"]'));
     var errorMessage = 'Please enter a valid phone number';
-    var errorMessageXPath = '//span[@class="help-block validation-message"]/span[contains(text(),"'
-    + errorMessage + '")]';
+    var errorMessageXPath = '//span[@class="help-block validation-message"]/span[contains(text(),"' +
+    errorMessage + '")]';
     phoneInput.sendKeys(1234567890).then(function () {
         driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath))
             .then(function (validationMessages) {
@@ -42,4 +43,3 @@ tap.test('validatePhoneNumber', function (t) {
             });
     });
 });
-
diff --git a/test/integration/teacher-registration/test_teacher_registration_username_step.js b/test/integration/teacher-registration/test_teacher_registration_username_step.js
index f8fb7e9a8..96c8d46ea 100644
--- a/test/integration/teacher-registration/test_teacher_registration_username_step.js
+++ b/test/integration/teacher-registration/test_teacher_registration_username_step.js
@@ -8,11 +8,12 @@ require('chromedriver');
 var seleniumWebdriver = require('selenium-webdriver');
 var tap = require('tap');
 
-//Set test url through environment variable
+// Set test url through environment variable
 var rootUrl = process.env.ROOT_URL || 'http://localhost:8333';
 
-//chrome driver
-var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build();
+// chrome driver
+var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome())
+    .build();
 
 tap.plan(5);
 
@@ -24,15 +25,15 @@ tap.beforeEach(function () {
     return driver.get(rootUrl + '/educators/register');
 });
 
-//an error message should appear for a username less than 3 characters long
-//input a username less than 3 characters and look for the validation message
+// an error message should appear for a username less than 3 characters long
+// input a username less than 3 characters and look for the validation message
 tap.test('checkAtLeastThreeCharacters', function (t) {
-    //open scratch in a new instance of the browser
+    // open scratch in a new instance of the browser
     driver.get('https://scratch.mit.edu/educators/register');
     var usernameInput = driver.findElement(seleniumWebdriver.By.name('user.username'));
     var errorMessage = 'Usernames must be at least 3 characters';
-    var errorMessageXPath = '//span[@class="help-block validation-message" and contains(text(),"'
-      + errorMessage + '")]';
+    var errorMessageXPath = '//span[@class="help-block validation-message" and contains(text(),"' +
+      errorMessage + '")]';
     usernameInput.sendKeys('hi').then(function () {
         driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)).then(function (validationMessages) {
             t.equal(validationMessages.length, 1);
@@ -41,31 +42,31 @@ tap.test('checkAtLeastThreeCharacters', function (t) {
     });
 });
 
-//usernames have to be unique
-//input a username that exists and check that an error message appears
+// usernames have to be unique
+// input a username that exists and check that an error message appears
 tap.test('checkUsernameExistsError', function (t) {
     var usernameInput = driver.findElement(seleniumWebdriver.By.name('user.username'));
     var passwordInput = driver.findElement(seleniumWebdriver.By.name('user.password'));
     var inputUsername = usernameInput.sendKeys('mres');
     var passwordClick = passwordInput.click();
     var errorMessage = 'Sorry, that username already exists';
-    var errorMessageXPath = '//span[@class="help-block validation-message" and contains(text(),"'
-      + errorMessage + '")]';
-    Promise.all([inputUsername, passwordClick]).then(function () {
-        var errorBubble = driver.wait(seleniumWebdriver.until.
-          elementLocated(seleniumWebdriver.By.xpath(errorMessageXPath)), 10000);
-        t.notEqual(errorBubble, undefined);
+    var errorMessageXPath = '//span[@class="help-block validation-message" and contains(text(),"' +
+      errorMessage + '")]';
+    Promise.all([inputUsername, passwordClick]).then(function () { // eslint-disable-line no-undef
+        var errorBubble = driver.wait(seleniumWebdriver.until
+            .elementLocated(seleniumWebdriver.By.xpath(errorMessageXPath)), 10000);
+        t.notEqual(errorBubble, undefined); // eslint-disable-line no-undefined
         t.end();
     });
 });
 
-//passwords must be at least 6 characters
-//find the validation message if the input password is less than 6 characters
+// passwords must be at least 6 characters
+// find the validation message if the input password is less than 6 characters
 tap.test('checkPasswordAtLeastSixCharacters', function (t) {
     var passwordInput = driver.findElement(seleniumWebdriver.By.name('user.password'));
     var errorMessage = 'Passwords must be at least six characters';
-    var errorMessageXPath = '//span[@class="help-block validation-message" and contains(text(),"'
-      + errorMessage + '")]';
+    var errorMessageXPath = '//span[@class="help-block validation-message" and contains(text(),"' +
+      errorMessage + '")]';
     passwordInput.sendKeys('hello').then(function () {
         driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)).then(function (validationMessages) {
             t.equal(validationMessages.length, 1);
@@ -74,15 +75,15 @@ tap.test('checkPasswordAtLeastSixCharacters', function (t) {
     });
 });
 
-//password cannot be "password"
-//find the validation message if the user inputs "password"
+// password cannot be "password"
+// find the validation message if the user inputs "password"
 tap.test('checkPasswordNotPassword', function (t) {
     driver.get('https://scratch.mit.edu/educators/register');
     var passwordInput = driver.findElement(seleniumWebdriver.By.name('user.password'));
-    //keeping "password" in messed with the xPath, may need to find a better way
+    // keeping "password" in messed with the xPath, may need to find a better way
     var errorMessage = 'Your password may not be';
-    var errorMessageXPath = '//span[@class="help-block validation-message" and contains(text(),"'
-      + errorMessage + '")]';
+    var errorMessageXPath = '//span[@class="help-block validation-message" and contains(text(),"' +
+      errorMessage + '")]';
     passwordInput.sendKeys('password').then(function () {
         driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)).then(function (validationMessages) {
             t.equal(validationMessages.length, 1);
@@ -91,24 +92,23 @@ tap.test('checkPasswordNotPassword', function (t) {
     });
 });
 
-//the username and password cannot be the same
-//find the validation message if the username and password match
+// the username and password cannot be the same
+// find the validation message if the username and password match
 tap.test('checkPasswordNotUsername', function (t) {
     driver.get('https://scratch.mit.edu/educators/register');
     var passwordInput = driver.findElement(seleniumWebdriver.By.name('user.password'));
     var usernameInput = driver.findElement(seleniumWebdriver.By.name('user.username'));
     var errorMessage = 'Your password may not be your username';
-    var errorMessageXPath = '//span[@class="help-block validation-message" and contains(text(),"'
-      + errorMessage + '")]';
+    var errorMessageXPath = '//span[@class="help-block validation-message" and contains(text(),"' +
+      errorMessage + '")]';
     var usernamePromise = usernameInput.sendKeys('educator');
     var passwordPromise = passwordInput.sendKeys('educator');
-    //wait for both inputs to have the same text, and check for validation message
-    Promise.all([usernamePromise, passwordPromise]).then(function () {
+    // wait for both inputs to have the same text, and check for validation message
+    Promise.all([usernamePromise, passwordPromise]).then(function () { // eslint-disable-line no-undef
         driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)).then(function (validationMessages) {
-            //there should be only one validation message
+            // there should be only one validation message
             t.equal(validationMessages.length, 1);
             t.end();
         });
     });
 });
-
diff --git a/test/integration/teacher-registration/test_teacher_registration_usescratch_step.js b/test/integration/teacher-registration/test_teacher_registration_usescratch_step.js
index 97235697c..f91baf590 100644
--- a/test/integration/teacher-registration/test_teacher_registration_usescratch_step.js
+++ b/test/integration/teacher-registration/test_teacher_registration_usescratch_step.js
@@ -10,11 +10,12 @@ var tap = require('tap');
 var utils = require('./teacher_registration_utils.js');
 var constants = utils.constants;
 
-//Set test url through environment variable
+// Set test url through environment variable
 var rootUrl = process.env.ROOT_URL || 'http://localhost:8333';
 
-//chrome driver
-var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build();
+// chrome driver
+var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome())
+    .build();
 
 tap.plan(3);
 
@@ -25,11 +26,11 @@ tap.tearDown(function () {
 tap.beforeEach(function () {
     driver.get(rootUrl + '/educators/register');
     return utils.fillUsernameSlide(driver, seleniumWebdriver)
-        .then(utils.fillDemographicsSlide.bind(this, driver, seleniumWebdriver))
-        .then(utils.fillNameSlide.bind(this, driver, seleniumWebdriver))
-        .then(utils.fillPhoneSlide.bind(this, driver, seleniumWebdriver))
-        .then(utils.fillOrganizationSlide.bind(this, driver, seleniumWebdriver))
-        .then(utils.fillAddressSlide.bind(this, driver, seleniumWebdriver));
+        .then(utils.fillDemographicsSlide.bind(this, driver, seleniumWebdriver)) // eslint-disable-line no-invalid-this
+        .then(utils.fillNameSlide.bind(this, driver, seleniumWebdriver)) // eslint-disable-line no-invalid-this
+        .then(utils.fillPhoneSlide.bind(this, driver, seleniumWebdriver)) // eslint-disable-line no-invalid-this
+        .then(utils.fillOrganizationSlide.bind(this, driver, seleniumWebdriver)) // eslint-disable-line no-invalid-this
+        .then(utils.fillAddressSlide.bind(this, driver, seleniumWebdriver)); // eslint-disable-line no-invalid-this
 });
 
 tap.test('checkCharacterCountIsCorrect', function (t) {
@@ -43,8 +44,8 @@ tap.test('checkCharacterCountIsCorrect', function (t) {
     });
 });
 
-//Inputs more than 300 characters and checks that the char count gets the class 'overmax'
-//which turns the text orange
+// Inputs more than 300 characters and checks that the char count gets the class 'overmax'
+// which turns the text orange
 tap.test('checkCharacterCountTurnsOrangeWhenTooLong', function (t) {
     var textarea = driver.findElement(seleniumWebdriver.By.name('useScratch'));
     var charCount = driver.findElement(seleniumWebdriver.By.xpath('//p[@class="char-count"]'));
@@ -59,8 +60,8 @@ tap.test('checkCharacterCountTurnsOrangeWhenTooLong', function (t) {
 tap.test('checkCharacterCountErrorAppersWhenTooLong', function (t) {
     var textarea = driver.findElement(seleniumWebdriver.By.name('useScratch'));
     var errorMessage = 'Description must be at most 300 characters';
-    var errorMessageXPath = '//span[@class="help-block validation-message" and contains(text(),"'
-      + errorMessage + '")]';
+    var errorMessageXPath = '//span[@class="help-block validation-message" and contains(text(),"' +
+        errorMessage + '")]';
     textarea.sendKeys(constants.loremIpsumTextLong).then(function () {
         driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)).then(function (validationMessages) {
             t.equal(validationMessages.length, 1);
diff --git a/test/localization/check_duplicate_strings.js b/test/localization/check_duplicate_strings.js
index 8b46cbe27..16f7ab71c 100644
--- a/test/localization/check_duplicate_strings.js
+++ b/test/localization/check_duplicate_strings.js
@@ -7,23 +7,23 @@ var tap = require('tap');
 
 var routes = require('../../src/routes.json');
 
-function noDuplicateValues (idsToCheck, name) {
-    var values = {};
-    for (var key in idsToCheck) {
+const noDuplicateValues = (idsToCheck, name) => {
+    let values = {};
+    for (const key in idsToCheck) {
         if (values.hasOwnProperty(idsToCheck[key])) {
             // duplicate values
-            //return false;
-            tap.fail(name + '.' + idsToCheck[key] + ' has duplicates');
+            // return false;
+            tap.fail(`${name}.${idsToCheck[key]} has duplicates`);
         } else {
             values[idsToCheck[key]] = key;
         }
     }
     tap.pass();
-    //return true;
-}
+    // return true;
+};
 
 tap.test('generalCheckForDuplicates', function (t) {
-    var ids = require(path.resolve(__dirname, '../../src/l10n.json'));
+    const ids = require(path.resolve(__dirname, '../../src/l10n.json')); // eslint-disable-line global-require
     noDuplicateValues(ids, 'general');
     t.end();
 });
@@ -35,11 +35,11 @@ for (var v in routes) {
     var subdir = routes[v].view.split('/');
     subdir.pop();
     var name = routes[v].name;
-    var uri = path.resolve(__dirname, '../../src/views/' + subdir.join('/') +'/l10n.json');
+    var uri = path.resolve(__dirname, '../../src/views/' + subdir.join('/') + '/l10n.json');
     try {
         var file = fs.readFileSync(uri, 'utf8');
         var ids = JSON.parse(file);
-        tap.test(name + 'CheckForDuplicates', function (t) {
+        tap.test(name + 'CheckForDuplicates', function (t) { // eslint-disable-line no-loop-func
             noDuplicateValues(ids, name);
             t.end();
         });
diff --git a/test/localization/check_string_ids.js b/test/localization/check_string_ids.js
index de4491d21..8fc019228 100644
--- a/test/localization/check_string_ids.js
+++ b/test/localization/check_string_ids.js
@@ -20,35 +20,34 @@ var tap = require('tap');
 var intlDirPath = path.resolve(__dirname, '../../intl/');
 var intlFiles = fs.readdirSync(intlDirPath);
 
-/**
+/*
  * Tells tap whether the test should pass or fail for a given file.
  * @param {string} fileName
  * @param {Object} missingMessageId
  * @param {Object} pagesMissingIds
  */
-function noMissingStrings (fileName, missingMessageId, pagesMissingIds) {
-    if (Object.keys(missingMessageId).length == 0) {
+const noMissingStrings = (fileName, missingMessageId, pagesMissingIds) => {
+    if (Object.keys(missingMessageId).length === 0) {
         tap.pass();
-    }
-    else {
+    } else {
         tap.fail(fileName + ' is missing string IDs');
         pagesMissingIds[fileName] = [];
         pagesMissingIds[fileName].push(missingMessageId);
     }
-}
+};
 
 var pagesWithLanguagesMissingIds = {};
 
 for (var i in intlFiles) {
     var file = intlFiles[i];
     var filePath = path.resolve(__dirname, '../../intl/' + file);
-    var pageMessagesString = fs.readFileSync(filePath,'utf8');
+    var pageMessagesString = fs.readFileSync(filePath, 'utf8');
 
     /**
      * To make the string of the file of the page.intl.js back into useable objects
      */
     var window = {};
-    var pageMessages = eval(pageMessagesString);
+    var pageMessages = eval(pageMessagesString); // eslint-disable-line no-eval
 
     /**
      * The goal is to compare the IDs for each language to the IDs for English,
@@ -61,8 +60,8 @@ for (var i in intlFiles) {
     for (var languageKey in pageMessages) {
         var currentLanguageObject = pageMessages[languageKey];
         for (var messageId in englishIdList) {
-            if (! (messageId in currentLanguageObject)) {
-                if (typeof messageIdNotInLanguage[languageKey] == 'undefined') {
+            if (!(messageId in currentLanguageObject)) {
+                if (typeof messageIdNotInLanguage[languageKey] === 'undefined') {
                     messageIdNotInLanguage[languageKey] = [];
                 }
                 messageIdNotInLanguage[languageKey].push(messageId);
diff --git a/test/localization/check_valid_json.js b/test/localization/check_valid_json.js
index c9cf26a46..efe6808ac 100644
--- a/test/localization/check_valid_json.js
+++ b/test/localization/check_valid_json.js
@@ -5,14 +5,14 @@ var tap = require('tap');
 var TRANSLATIONS_PATTERN = './node_modules/scratchr2_translations/www/**/*.json';
 var files = glob.sync(TRANSLATIONS_PATTERN);
 
-function checkJson (data, name) {
+const checkJson = (data, name) => {
     try {
         JSON.parse(data);
     } catch (e) {
         tap.fail(name + ' has invalid Json.\n');
     }
     tap.pass();
-}
+};
 
 files.forEach(function (f) {
     tap.test('check valid json', function (t) {
diff --git a/test/unit/test_fastly_config_methods.js b/test/unit/test_fastly_config_methods.js
index a75e0afd6..81d96d11a 100644
--- a/test/unit/test_fastly_config_methods.js
+++ b/test/unit/test_fastly_config_methods.js
@@ -1,6 +1,6 @@
 var defaults = require('lodash.defaults');
 var fastlyConfig = require('../../bin/lib/fastly-config-methods');
-var route_json = require('../../src/routes.json');
+var routeJson = require('../../src/routes.json');
 var tap = require('tap');
 
 var testRoutes = [
@@ -20,7 +20,7 @@ var testRoutes = [
     }
 ];
 
-var routes = route_json.map(function (route) {
+var routes = routeJson.map(function (route) {
     return defaults({}, {pattern: fastlyConfig.expressPatternToRegex(route.pattern)}, route);
 });
 var extraAppRoutes = [
@@ -28,7 +28,7 @@ var extraAppRoutes = [
     // TODO: Should this be added for every route?
     '/\\?',
     // View html
-    '/[^\/]*\.html$'
+    '/[^/]*.html$'
 ];
 
 
diff --git a/webpack.config.js b/webpack.config.js
index 484be5c20..b9976df34 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -1,36 +1,43 @@
-var autoprefixer = require('autoprefixer');
-var CopyWebpackPlugin = require('copy-webpack-plugin');
-var defaults = require('lodash.defaults');
-var HtmlWebpackPlugin = require('html-webpack-plugin');
-var gitsha = require('git-bundle-sha');
-var path = require('path');
-var webpack = require('webpack');
+const autoprefixer = require('autoprefixer');
+const CopyWebpackPlugin = require('copy-webpack-plugin');
+const defaults = require('lodash.defaults');
+const HtmlWebpackPlugin = require('html-webpack-plugin');
+const gitsha = require('git-bundle-sha');
+const path = require('path');
+const webpack = require('webpack');
 
-var routes = require('./src/routes.json');
+let routes = require('./src/routes.json');
+const templateConfig = require('./src/template-config.js'); // eslint-disable-line global-require
 
 if (process.env.NODE_ENV !== 'production') {
-    routes = routes.concat(require('./src/routes-dev.json'));
+    routes = routes.concat(require('./src/routes-dev.json')); // eslint-disable-line global-require
 }
 
-var VersionPlugin = function (options) {
+let VersionPlugin = function (options) {
     this.options = options || {};
     return this;
 };
+
 VersionPlugin.prototype.apply = function (compiler) {
-    var addVersion = function (compilation, versionId, callback) {
+    const addVersion = function (compilation, versionId, callback) {
         compilation.assets['version.txt'] = {
-            source: function () {return versionId;},
-            size: function () {return versionId.length;}
+            source: function () {
+                return versionId;
+            },
+            size: function () {
+                return versionId.length;
+            }
         };
         callback();
     };
-    var plugin = this;
+    const options = this.options;
+    
     compiler.plugin('emit', function (compilation, callback) {
-        var sha = process.env.WWW_VERSION;
-        if (!sha) {
-            gitsha(plugin.options, function (err, sha) {
+        const sha = process.env.WWW_VERSION;
+        if (!sha) { // eslint-disable-line no-negated-condition
+            gitsha(options, function (err, _sha) {
                 if (err) return callback(err);
-                return addVersion(compilation, sha, callback);
+                return addVersion(compilation, _sha, callback);
             });
         } else {
             return addVersion(compilation, sha, callback);
@@ -39,7 +46,7 @@ VersionPlugin.prototype.apply = function (compiler) {
 };
 
 // Prepare all entry points
-var entry = {
+let entry = {
     common: [
         // Vendor
         'raven-js',
@@ -53,7 +60,7 @@ var entry = {
 };
 routes.forEach(function (route) {
     if (!route.redirect) {
-        entry[route.name] = './src/views/' + route.view + '.jsx';
+        entry[route.name] = `./src/views/${route.view}.jsx`;
     }
 });
 
@@ -66,26 +73,45 @@ module.exports = {
         filename: 'js/[name].bundle.js'
     },
     module: {
-        loaders: [
+        rules: [
             {
-                test: /\.jsx$/,
-                loader: 'babel',
-                query: {
-                    presets: ['es2015','react']
-                },
-                include: path.resolve(__dirname, 'src')
-            },
-            {
-                test: /\.json$/,
-                loader: 'json-loader'
+                test: /\.jsx?$/,
+                loader: 'babel-loader',
+                include: path.resolve(__dirname, 'src'),
+                options: {
+                    presets: ['es2015', 'react']
+                }
             },
             {
                 test: /\.scss$/,
-                loader: 'style!css!postcss-loader!sass'
+                use: [
+                    'style-loader',
+                    'css-loader',
+                    {
+                        loader: 'postcss-loader',
+                        options: {
+                            plugins: function () {
+                                return [autoprefixer({browsers: ['last 3 versions', 'Safari >= 8', 'iOS >= 8']})];
+                            }
+                        }
+                    },
+                    'sass-loader'
+                ]
             },
             {
                 test: /\.css$/,
-                loader: 'style!css!postcss-loader'
+                use: [
+                    'style-loader',
+                    'css-loader',
+                    {
+                        loader: 'postcss-loader',
+                        options: {
+                            plugins: function () {
+                                return [autoprefixer({browsers: ['last 3 versions', 'Safari >= 8', 'iOS >= 8']})];
+                            }
+                        }
+                    }
+                ]
             },
             {
                 test: /\.(png|jpg|gif|eot|svg|ttf|woff)$/,
@@ -94,22 +120,21 @@ module.exports = {
         ],
         noParse: /node_modules\/google-libphonenumber\/dist/
     },
-    postcss: function () {
-        return [autoprefixer({browsers: ['last 3 versions', 'Safari >= 8', 'iOS >= 8']})];
-    },
     node: {
         fs: 'empty'
     },
     plugins: [
         new VersionPlugin({length: 5})
     ].concat(routes
-        .filter(function (route) {return !route.redirect;})
+        .filter(function (route) {
+            return !route.redirect;
+        })
         .map(function (route) {
             return new HtmlWebpackPlugin(defaults({}, {
                 title: route.title,
                 filename: route.name + '.html',
                 route: route
-            }, require('./src/template-config.js')));
+            }, templateConfig));
         })
     ).concat([
         new CopyWebpackPlugin([
@@ -117,17 +142,17 @@ module.exports = {
             {from: 'intl', to: 'js'}
         ]),
         new webpack.optimize.UglifyJsPlugin({
-            compress: {
-                warnings: false
-            }
+            sourceMap: true
         }),
         new webpack.DefinePlugin({
             'process.env.NODE_ENV': '"' + (process.env.NODE_ENV || 'development') + '"',
             'process.env.SENTRY_DSN': '"' + (process.env.SENTRY_DSN || '') + '"',
             'process.env.API_HOST': '"' + (process.env.API_HOST || 'https://api.scratch.mit.edu') + '"',
-            'process.env.SCRATCH_ENV': '"'+ (process.env.SCRATCH_ENV || 'development') + '"'
+            'process.env.SCRATCH_ENV': '"' + (process.env.SCRATCH_ENV || 'development') + '"'
         }),
-        new webpack.optimize.CommonsChunkPlugin('common', 'js/common.bundle.js'),
-        new webpack.optimize.OccurenceOrderPlugin()
+        new webpack.optimize.CommonsChunkPlugin({
+            name: 'common',
+            filename: 'js/common.bundle.js'
+        })
     ])
 };