Create Chrome DevTools Extension using React.js

Hello, Medium developers community! It’s time to write my first post here. Today I would like to share with you my experience of creating Google Chrome DevTools extension. I’ve recently done one small internal project for our company and realised how it’s easy and really nice to develop your helpful extension using modern tools! So probably this post will give you some inspiration to start your own project. Today me and you are creating pretty straightforward application just to see how easy it is. We are going to make our own Google Chrome developer tools which will display a color picker.
If you prefer do not spend time on reading the post, you can go directly to project’s repo on Github, switch to branch
color-pickerand explore the code. Otherwise, let’s start from the beginning!
Structuring the project
First of all we need to think about files structure for our app. Frankly, It’s completely up to you how to organise your files. What is important is to have them built in the single directory in the end. So I’ll going to have next structure :
src/- /components/- — /ColorPicker/- — — /ColorPicker.jsx- — — /styles.scss- — /index.jsx- /devtools/- — /devtools.html- — /devtools.js- /index.html.babelrcgulpfile.babel.jsmanifest.jsonpackage.jsonwebpack.config.js
Now we are ready to initialize our project and install dependencies. You could do it manually through running npm init and installing all dependencies via npm install. Or you can simply take my package.json file as example, place it to the project’s root folder and run npm install in your console.
/* ./package.json */{
"name": "ColorPickerDevTools",
"version": "0.0.1",
"description": "Example of using React for developing Google Chrome DevTools Extension",
"scripts": {
"build": "./node_modules/.bin/gulp build",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+ssh://git@github.com:yurist38/chromeDevToolsReactBoilerplate.git"
},
"author": "Yuri Drabik <yurist38@mail.ru>",
"devDependencies": {
"autoprefixer": "^7.1.1",
"babel-core": "^6.25.0",
"babel-loader": "^7.1.1",
"babel-polyfill": "^6.23.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"css-loader": "^0.28.4",
"eslint": "^4.1.1",
"eslint-plugin-react": "^7.1.0",
"gulp": "github:gulpjs/gulp#4.0",
"gulp-rm": "^1.0.4",
"moment": "^2.18.1",
"node-sass": "^4.5.3",
"postcss": "^6.0.6",
"postcss-cssnext": "^3.0.2",
"postcss-loader": "^2.0.6",
"prop-types": "^15.5.10",
"react": "^15.6.1",
"react-color": "^2.13.4",
"react-dom": "^15.6.1",
"sass-loader": "^6.0.6",
"style-loader": "^0.18.2",
"webpack": "^3.0.0"
}
}
Also once our app is built we will have ./build directory with bunch of files what you can directly install into your Chrome browser as an extension.
Creating React.js application
I suggest to continue with preparing our React.js application. Let’s say, we have one child component ColorPicker:
/* ./src/components/ColorPicker/ColorPicker.jsx */import React from 'react';
import PropTypes from 'prop-types';
import { SketchPicker } from 'react-color';
import styles from './styles';const ColorPicker = ({ color, onChangeColor }) => (
<div className={styles.container} style={({backgroundColor: color})}>
<SketchPicker color={color} onChange={onChangeColor} />
</div>
);ColorPicker.propTypes = {
username: PropTypes.string,
onChangeColor: PropTypes.func
};export default ColorPicker;
It includes some styles:
/* ./src/components/ColorPicker/styles.scss */html, body {
margin: 0;
padding: 0;
}.container {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
min-height: 320px;
overflow: auto;
}
And, of course, we need to create app root component. I’ll call it index.jsx and place into ./src/components folder.
/* ./src/components/index.jsx */import React, { Component } from 'react';
import { render } from 'react-dom';
import ColorPicker from './ColorPicker/ColorPicker';class App extends Component {
constructor() {
super();
this.state = {
color: '#cccccc'
}
}onChangeColor(color) {
this.setState({color: color.hex});
}render() {
return (
<ColorPicker
color={this.state.color}
onChangeColor={this.onChangeColor.bind(this)}
/>
);
}
}render(
<App />,
document.getElementById('app')
);
So, first part is done! Our awesome React.js application is almost here (of course, it’s very simple. Use it as an example and develop your own cool stuff!).
Time to create an extension itself!
Next step is creating an html page, which will be specified in manifest.json file as entrypoint and loaded into our new devtools panel. It’s also very straightforward, simple html skeleton including devtools.js script:
/* ./src/devtools/devtools.html */<!DOCTYPE html>
<html>
<head>
<script src="devtools.js"></script>
</head>
</html>
/* ./src/devtools/devtools.js */chrome.devtools.panels.create(
'ColorPicker', // title for the panel tab
null, // you can specify here path to an icon
'index.html', // html page for injecting into the tab's content
null // you can pass here a callback function
);
Manifest
manifest.json is a main declaration file of our extension. You can find full description of manifest file’s format here. In our case it’s gonna be very short and simple as well:
/* ./manifest.json */{
"manifest_version": 2,"name": "ColorPicker DevTools",
"description": "Demo extension for Google Chrome browser",
"version": "1.0","permissions": [
"*://*/*"
],
"devtools_page": "devtools.html"
}
Automate building process
And last step is doing some automation with Webpack and Gulp. First one we are going to use for transpiling and bundling our React.js app.
/* ./webpack.config.js */const path = require('path');module.exports = {
entry: [
path.resolve('./src/components/index.jsx')
],
output: {
path: path.resolve('./build/'),
filename: 'app.js',
publicPath: '.'
},
module: {
loaders: [
{
test: /\.jsx/,
include: path.resolve('./src/components/'),
loader: 'babel-loader'
},
{
test: /\.s?css$/,
use: [
{loader: 'style-loader'},
{
loader: 'css-loader?importLoaders=1',
query: {
modules: true,
localIdentName: '[name]__[local]___[hash:base64:5]'
}
},
{loader: 'sass-loader'},
{
loader: 'postcss-loader',
options: {
plugins: function () {
return [
require('postcss-cssnext')
];
}
}
}
]
}
]
},
resolve: {
extensions: ['.js', '.jsx', '.scss', '.css']
}
};
For correct handling ES6 syntax we need to specify babel preset in ./.babelrc file in root folder:
/* ./.babelrc */{
"presets": ["es2015", "react"]
}
And last, we will use gulp for serving all needed files together in the ./build/ folder. Let’s write a few tasks:
/* ./gulpfile.babel.js */import gulp from 'gulp';
import rm from 'gulp-rm';
import webpack from 'webpack';
import fs from 'fs';const paths = {
build: './build'
};/* CLEAN */
gulp.task('clean', () => {
return gulp
.src(`${paths.build}/**/*`, {read: false})
.pipe(rm());
});/* BUILD COMPONENTS */
gulp.task('build:app', (done) => {
webpack(require('./webpack.config.js'), done);
});/* COPY FILES INTO BUILD FOLDER */
const files = [
'./manifest.json',
'./src/index.html',
'./src/devtools/devtools.html',
'./src/devtools/devtools.js'
];
gulp.task('copy:static', (done) => {
gulp
.src(files)
.pipe(gulp.dest(paths.build))
.on('end', done);
});/* BUILD */
gulp.task('build', gulp.series([
'clean',
'build:app',
'copy:static'
]));
That’s it. Our own devtools extension is ready now! Once you have done withnpm build you can install extension into your browser. You can find simple instruction how to do that in the repo overview.
Demo

Conclusion
As I mentioned before it’s just basic example. I’m keep learning how to write chrome extensions using modern tools like React.js. And if you guys are interested to know more about that as well, please let me know in comments. It’s gonna be a reason for me to share more examples as well as advanced techniques.
Source code
github.com/yurist38/chromeDevToolsReactBoilerplate/tree/color-picker
Happy weekend everyone!
