I am rather smitten with Aurelia but it’s hard to deny the popularity of React. To avoid a situation where it’s React or nothing, to get the best of both worlds, we can use React inside of Aurelia.
In Angular, we would have done this using a custom directive. In Aurelia, we are going to be doing the same, but instead, we will be creating a custom element which achieves the same thing as an Angular directive, albeit in a more clean ESNext and Aurelia-like way.
Installing React
Before we proceed we need to make sure that React is installed. We can do this by installing it: npm install react --save
— we also need to install the React DOM package as well for rendering our custom components: npm install react-dom --save
In the below code example we are going to create a custom element, which will pass data through to React and also has the added advantage of re-rendering itself when the data changes.
Create a custom element
For the sake of sticking with the demo, I recommend saving this file as “react-element.js” or honestly whatever you want to call it. The filename is irrelevant, I recommend sticking with this name for the sake of the article.
import React from 'react';
import ReactDOM from 'react-dom';
import {customElement, inject, bindable, noView} from 'aurelia-framework';
import MyReactElement from './components/my-react-element';
@noView()
@inject(Element)
@bindable('data')
@customElement('react-element')
export class ReactElement {
reactComponent = {};
constructor(element) {
this.element = element;
}
render() {
this.reactComponent = ReactDOM.render(
<MyReactElement data={this.data} />,
this.element
);
}
bind() {
this.render();
}
/**
* Data Changed
*
* An automatic callback function when our "data"
* bindable value changes. We need to rebind the React
* element to get the new data from the ViewModel.
*
* @param {any} newVal The updated data
* @returns {void}
*
*/
dataChanged(newVal) {
this.bind();
}
}
Creating a React component
For the above example, let’s create our React component. It will not do much, but it will work nonetheless.
Save the following file as my-react-element.js
as per the above example and instructions.
import React from 'react';
export default class MyReactElement extends React.Component {
constructor(props) {
super(props);
}
render() {
if (!this.props.data.length) {
return null;
}
return (
<div>
<hr />
Hello, I am a React component being rendered inside of Aurelia.
This file is located in: src/components/react-components/my-react-element.jsx and is being included from within src/components/custom-elements/react-element.js
Let's loop through any provided data:
<ul>
{
this.props.data.map(item => {
return <li key={item.key}>{item.name}</li>
})
}
</ul>
</div>
);
}
}
MyReactElement.defaultProps = { data: [] };
Putting it together
The ViewModel
All the ViewModel does is supply our view using the React custom element with data.
Save this file as whatever name you like. For the purposes of this example, I recommend choosing react-example.js
– so everything stays inline with the rest of this post.
export class ReactExample {
someData = [];
constructor() {
this.someData = [
{key: 'testkey', name: 'Dwayne'},
{key: 'testkey2', name: 'Rod'},
{key: 'testkey3', name: 'Todd'}
];
}
}
The View
Okay, now that we have our ViewModel, we need our matching view. The name of the View HTML file needs to match the name of the above ViewModel Javascript file, not the class name. So if you saved the above as “react-example.js” – we will save this View as “react-example.html”
<template>
<require from="components/react-element"></require>
<h1>This is the homepage</h1>
<h2>Let's render a React component</h2>
<react-element data.bind="someData"></react-element>
</template>
Source Code & Demo
You can download and view the source code here on Github. You can also view a demo of the code working here.
Nice article – PS, your Create the View code example about the template is empty.
Just found out about how cool react is (particularly for replacing ugly jquery stuff). I’d also like to know how how well Riot 2 works with Aurelia. It’s great how it’s not too hard to use web components in Aurelia.
Hi, cool write up 🙂
Just thought maybe it’s worth mentioning that with the second example which uses decorators and JSX, you need to install the jsx plugin (http://github.com/floatdrop/plugin-jsx) and add
/** @jsx React.DOM */
to the first line of your reactel.jsThanks again for writing such a cool and informative post Dwayne, helped me out a lot!
@Zen,
Because Babel is now including JSX support, and Aurelia recommends and starts you off on Babel, there shouldn’t be any need for additional plugins or pragmas. I’m not 100% sure if that’s true today but it’s definitely around the corner. The plugin you linked to has already deprecated in favor of Babel.
Here a working example https://github.com/cscleison/AureliaReactPOC
Awesome article Dwayne it really shows the benefits of Aurelias open architecture and how easily different view engines can be plugged in. Also great work cleison with creating the Github repo.
One great Addon for the article might be to show how Aurelia’s two-way-binding may be used together with React. I’ve created a PR for the above mentioned GitHub Repo depicting this feature.
Great post, cool to see that this is all possible.
+1 for how this would work with http://riotjs.com/
I am new to aurelia and react and trying to integrate the two but am having difficulty. Is there a low level guide to understand what’s happening here? Or a working example that I actually see it in action?
I have created a Github repository which I am about to add in some examples for using React in Aurelia. I apologise for any confusion, I realise this blog post does not delve into details too much and for some, it might be confusing.
Sit tight everyone and thank you for your patience.
How would I do more than one component using the same jsx?
Hi C. Daniel,
In the exported component file just define multiple components and then make sure you export them. You can then import one or more components wherever you include the JSX file.
I am a bit confused.. Why would you use react inside Aurelia?
The only explaination I got was:
“Due to the fact that Aurelia itself does not ship with its own efficient virtual DOM diffing algorithm for rendering composed views and templates, we can use React.js for the heavier UI stuff.”
Could you give some examples? What is this heavier UI stuff that Aurelia cannot handle on its own?
I’m trying to work through this same setup except using Typescript (and TSX files in place of JSX). So far, I get a “syntax error” on load somewhere inside SystemJS.
Turns out the problem was the TSX not being compiled and retrieved correctly. The vanilla code from your GitHub repo works fine (on Node v6, though not on v8 thanks to node-sass incompatibility). And from what I can see starting from the Aurelia CLI is going to be very difficult at this point. I’m going to try next starting from the latest ES skeleton and reproducing what you have. If I can get *that* working I might be able to reproduce that in a Typescript-based skeleton. Here’s hoping 🙂
How will this work with components already created in React? Would we have to extend each React component that we use?
Thanks!
Just putting this out here in case anyone (still) wants to try this:
This repo, which is heavily based on the article above, helped me getting this to work.
Note that you might need to update some things depending on your needs (It’s all old code) – in my case I had to update the babel plugins & had to separate the loaders, as we are using ts-loader for .js files (so add babel-loader for .jsx files)
https://github.com/allencoded/aurelia-react-skeleton/wiki/Aurelia-with-React-using-Webpack