(Updated: )
/ #alpinejs #javascript 

Run Alpine.js inside of React

Alpine.js is well-suited for building widgets and easily creating client-side experiences for websites that are mainly server-side rendered or static.

Good news: it is possible (at the time of writing with Alpine.js 2.x) to integrate Alpine.js with React trivially.

Alpine.js is “A rugged, minimal framework for composing JavaScript behavior in your markup.”. It can be included simply with a script tag from a CDN and weights in under 5kb.

Caleb Porzio, Alpine.js’ creator, likens it to “Tailwind for JavaScript”.

Now one of the great things about Tailwind is that the examples provided are very much copy-pasteable.

If Alpine.js widgets ever pick up, integrating them in a React application might be useful.

In this post we’ll see how to run Alpine.js inside of a React application.

To see the full demo/code repo head to github.com/HugoDF/react-alpinejs.

Table of Contents

Introduction & Setup

To integrate Alpine.js and React we’ll need a project where both are installed. For a minimal React project setup I use Parcel as per github.com/HugoDF/react-minimal-starter-parcel.

On top of that we’ll need to install alpinejs (the starter project comes with react, react-dom and parcel-bundler).

yarn add alpinejs

Next, Alpine.js needs to be included in the bundle, in main.js we can add an import.

import React from 'react';
import 'alpinejs';

// Rest of React App/ReactDOM setup code.

There are a few gotchas to using Alpine.js inside of a React project.

Register x-data and x-init functions as window properties

Alpine.js looks for x-init and x-data functions on the global scope (ie. window). By default, bundlers will scope the functions to the current module (instead of window).

That means you’ll need to define your functions as window properties, like so:

// main.js, imports are omitted for brevity.
window.counter = function() {
  return {
    count: 0,
    inc() {
      this.count += 1;
    }
  };
};

Note: arrow functions also work.

Use a template string + dangerouslySetInnerHTML to render Alpine.js templates

React/JSX/Parcel/Webpack don’t like :, you’ll get Namespace tags are not supported by default. React's JSX doesn't support namespace tags. when trying to use x-on:eventType event bindings or the shorthand for property bindings (without x-bind eg. :key). @ which is used as shorthand for x-on: is also invalid.

In order to render an Alpine.js template, we need to avoid the JSX compiler. In order to do this, we can make the template a string:

// main.js
// imports and window.counter definition omitted
const alpineTemplate = `<div x-data="counter()">
  <button @click="inc()">Increment</button>
  <p x-text="count"></p>
</div>`;
// ReactDOM bootstrap omitted

This way, @ and : don’t cause any issues.

To render this template, we’ll now dangerouslySetInnerHTML to this HTML string.

// main.js
// imports and window.counter definition omitted
const AlpineWidget = () => (
  <div dangerouslySetInnerHTML={{__html: alpineTemplate}} />
);

const App = () => (
  <>
    <h2>Running Alpine.js inside React</h2>
    <AlpineWidget />
  </>
);
// ReactDOM bootstrap omitted

Putting this all together gets us our Alpine.js counter widget rendering from inside a React application.

Alpine.js React Demo Counter Widget

unsplash-logoVladimir Kudinov

Author

Hugo Di Francesco

Co-author of "Professional JavaScript", "Front-End Development Projects with Vue.js" with Packt, "The Jest Handbook" (self-published). Hugo runs the Code with Hugo website helping over 100,000 developers every month and holds an MEng in Mathematical Computation from University College London (UCL). He has used JavaScript extensively to create scalable and performant platforms at companies such as Canon, Elsevier and (currently) Eurostar.

Interested in Alpine.js?

Subscribe to Alpine.js Weekly. A free, once–weekly email roundup of Alpine.js news and articles