If you are like me, coming from React into the world of Shopify, I’m sure you’ve wanted to implement a React package like Toastify into your theme. Unfortunately, is not as simple as if you were on a NextJS or CRA app but it’s definitely possible.
In this tutorial we will be creating React components using our own Babel + Webpack configuration. As a bonus, we will also be adding Tailwind. There’s a lot of steps involved so let’s get started.
Requirements
Yarn (Recommended, but you can also use NPM)
0. Themekit
We will assume you’ve already configured Themekit to properly deploy your files to Shopify. If you need the steps please follow the official guide and come back here
1. Install Compilers
Let’s start by creating a new package.json
which will hold all of our dependencies:
npm init -y
Once you have that ready, add Babel & Webpack using the following script
yarn add --dev @babel/cli @babel/core @babel/preset-env @babel/preset-react babel-loader css-loader mini-css-extract-plugin webpack webpack-cli
2. Configuration
We need to create two configuration files:
.babelrc
{
"presets":[
"@babel/preset-env",
"@babel/preset-react"
]
}
webpack.config.js
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
mode: "production",
entry: {
// Components belong here
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ["babel-loader"],
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
],
}
]
},
plugins: [
new MiniCssExtractPlugin({
// Source file
filename: "style.css",
// Destination file, feel free to change the name
chunkFilename: "style.css",
}),
],
output: {
filename: "[name].bundle.js",
path: path.resolve(__dirname, "assets"),
}
};
Lastly, let’s add two scripts to our package.json to make development easier
{
"scripts": {
"build": "webpack --progress --color",
"watch": "webpack --progress --color --watch"
}
}
Now, if you run yarn build
it will compile everything once, if you run yarn watch
it will compile every time you update a component.
3. Add React Component
Now time for the fun part, add React & ReactDom
yarn add react react-dom
I usually create a folder on the root theme called dev
and put all my files in there. Create the folder, an empty stylesheet (more on this later) and a new JS file.
dev/style.css
/* This file is empty for now */
dev/like-button.js
class LikeButton extends React.Component {
constructor(props) {
super(props);
this.state = {liked: false};
}
render() {
if (this.state.liked) {
return 'You liked this.';
}
return (
);
}
}
let domContainer = document.querySelector('#like_button_container');
ReactDOM.render(<LikeButton/>, domContainer);
Once that’s ready, let’s add our new component to the webpack config. I like naming my components starting with react- because this prevents me from overriding a theme file by mistake, plus it’s easier to identify. Let’s also add our empty stylesheet.
webpack.config.js
{
"entry": {
"style": "./dev/style.css", // Our empty stylesheet
"react-like-button": "./dev/like-button.js" // One line per component
}
}
Now our component is ready to be used on Shopify.
Create a new snippet, this will hold our React Component
snippets/react-like-button.liquid
<div id="like_button_container">div>
{{ 'react-like-button.bundle.js' | asset_url | script_tag }}
Compile using yarn build
and that’s it! You can now use {% include 'react-like-button' %}
anywhere in your theme. For testing purposes you may add it on your layout/theme.liquid file
4. Third party React Components
Now that you’re using React you may also implement external packages. Let’s add Toastify
yarn add react-toastify
dev/like-button.js
import React from "react";
import ReactDOM from "react-dom"
import { ToastContainer, toast } from 'react-toastify';
class LikeButton extends React.Component {
render() {
return (
<>
<button onClick={() => toast('? You liked this!')}>
Like
button>
<ToastContainer />
);
}
}
let domContainer = document.querySelector('#like_button_container');
ReactDOM.render(<LikeButton/>, domContainer);
I know what you are thinking, ‘where’s the style?’. This is where our MiniCssExtractPlugin comes in handy. Import the component’s style into our empty stylesheet.
dev/style.css
@import 'react-toastify/dist/ReactToastify.css';
Now, update the component liquid, and you’re all set!
snippets/react-like-button.liquid
<div id="like_button_container"></div>
{{ 'react-like-button.bundle.js' | asset_url | script_tag }}
{{ 'style.css' | asset_url | stylesheet_tag }}
5. Tailwind Support
As I’m sure you’ve heard, Tailwind is a great tool for creating gorgeous UIs with little to no css so let’s include it into our app.
We will be needing a few extra dependencies
yarn add --dev tailwindcss postcss-loader autoprefixer
5.1 Create Tailwind config file
tailwind.config.js
module.exports = {
purge: {
enabled: true, // Auto Purge
content: ['./dev/*.js'] // Purge our components
},
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
};
5.1.1 Tailwind on Liquid files [Optional]
If you want to use Tailwind classes on your liquid files make sure you add them to the purgeable array
5.2 Create Postcss config file
postcss.config.js
module.exports = {
plugins: [
require('tailwindcss'),
require('autoprefixer'),
],
};
5.3 Add Postcss to Webpack config
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"postcss-loader" <--- Add This line
]
}
5.4 Import tailwind from style.css
dev/style.css
@import 'react-toastify/dist/ReactToastify.css';
@tailwind base;
@tailwind components;
@tailwind utilities;
5.5 Done!
You may now use all tailwind classes and don’t worry, we’ve enabled purge so classes you are not using will be removed automatically.
Update your component just to make sure everything works. snippets/react-like-button.liquid
import React from "react";
import ReactDOM from "react-dom"
import { ToastContainer, toast } from 'react-toastify';
class LikeButton extends React.Component {
render() {
return (
<>
<button className="px-12 py-4 bg-blue-900 hover:bg-blue-600 text-white" onClick={() => toast('? You liked this!')}>
Like
button>
<ToastContainer />
);
}
}
let domContainer = document.querySelector('#like_button_container');
ReactDOM.render(<LikeButton/>, domContainer);
5.6 Done!
We’ve created a repo which holds all the configuration explained here. Take a look!