Laravel PHP Framework Discussions
Using React JS with Laravel / Lumen (With Bonus Material...UI)
Hello, all.
Over the past few weeks, I've been learning all kinds of new things. I made the move from FuelPHP to Laravel, and from being primarily a back-end developer to full stack. It's been quite a trip so far, and I know I have a long ways to go, but I also know how great it is to have guides (and references to good tutorials) for learning something new. To that end, I've decided to make a little tutorial for those looking to set up React with Laravel.
For now, I'll assume you have a Laravel project already (if you don't, checkout Laravel's installation documentation. Also, there are already great tutorials about using React, so I won't go into that here, but I'll do my best to reference the material I used to learn. Please don't take anything I say as gospel; I'm still learning. You've been warned.
Also, I'm using Lumen for the project I'm working on, so there may be subtle differences. I'll point them out where I know about them, but please let me know if I missed something.
Caveats aside, let's jump in, shall we?
The View at 10,000 Feet
Before we dig into the code, let's take a minute to think about the project structure. Laravel is a back-end framework, React is a great way to make SPA (lots of resources here). What this means, though, is we'll be doing a lot of front-end navigation. To do that, we'll use react-router. Note that we'll be creating our views all in the front-end, so Laravel views are all out of the picture (unless you get creative, of course).
Configuring Laravel
Okay, so I spoke just a moment too soon when I said "Laravel views are all out of the picture." We do still need a way to serve our base view which will include the JavaScript we'll be writing.
Let's create our base view! In /resources/views/ make a file (I called mine layout.php):
<html>
<head>
<link rel="stylesheet" type="text/css" href="css/app.css" />
</head>
<body>
<div id="root"></div>
<!-- Ideally, we'd included Babel from a CDN as well, but I couldn't get it to not error out for some reason...ah, well. We'll compile on before serving, then, eh? No worries. -->
<!-- <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/6.1.19/browser.min.js"></script> -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react-dom.js"></script>
<script type="text/javascript" src="/js/main.js"></script>
</body>
</html>
As you can see, this includes React and ReactDOM from a CDN. You can include it from your node_modules as well, but I did this for development. Also note that we're including /js/main.js. This is relative to the /public/ folder. It doesn't exist yet, but we'll get there in just a moment.
Routing
As mentioned in the CSS Tricks tutorial for react-router, we'll want to make a wildcard route that just serves up our layout.php when using browserHistory. The react-router will figure out if it's a "real" route or not later.
I found that here's a part where Laravel and Lumen differ slightly (but it's an important distinction). Open up your /app/Http/routes.php.
In Laravel, we can use regular expressions as parameters, but in Lumen...we can't.
Lumen
// A frustrating way to return the stub html file and let React handle the rest.
// For use with react-router's browserHistory history management system.
$app->get('/', function () use ($app) {
return view('layout');
});
$app->get('/{route1}', function () use ($app) {
return view('layout');
});
$app->get('/{route1}/{route2}', function () use ($app) {
return view('layout');
});
$app->get('/{route1}/{route2}/{route3}', function () use ($app) {
return view('layout');
});
Laravel
(haven't tested, but I think this would work--found this old answer)
$app->get('(.*)', function () use ($app) {
return view('layout');
});
Our gulpfile.js
This one is really simple.
var elixir = require('laravel-elixir');
elixir(function(mix) {
mix.browserify('main.jsx');
});
mix.browserify() takes the file specified (relative to /resources/assets/js/) and works its magic. Remember the /js/main.js we referenced in our stub HTML view earlier? When we run gulp from the command line, browserify will make that file.
And Finally, Using React
In /resources/assets/js/main.jsx, we'll start using React to build our application:
// Import React
import injectTapEventPlugin from 'react-tap-event-plugin';
import reactDOM from 'react-dom';
import {browserHistory, Router, Route, IndexRoute, Link} from 'react-router';
// I've got some components created already
import App from "./components/app/app.jsx";
import Home from "./pages/home.jsx";
// Needed for onTouchTap
// Check this repo:
// https://github.com/zilverline/react-tap-event-plugin
injectTapEventPlugin();
// Attach React to the root <div>
reactDOM.render((
<Router history={browserHistory}>
<Route path="/" component={App}>
<IndexRoute component={Home} />
<Route path="students" />
<Route path="reports" />
</Route>
</Router>
), document.getElementById('root')
);
You'll notice I've got a couple components created already in ./components/ and ./pages/. I just made those folders because they made sense to me. Reusable components go in ./components/, and the different pages that use those components go in ./pages/. Note the ./--this is relative to /resources/assets/js/.
Also, I've got this strange injectTapEventPlugin(); thing going on. What's up with that? Well, I'm using Material-UI, and it requires react-tap-event-plugin, so...that's that. Basically, it'll solve some problems with iOS, from what I gather.
Okay, so we've got that up and running (assuming, of course, you've created some blank components for App and Home for now)...Now what?
Bonus Material(-UI)
danyboyhd asked that I would post some installation material for Material-UI on Laravel, so here you have it. It's not anything special with Laravel, really. The tricky part with Laravel is setting up the routes for react-router.
In the main.jsx that we made a moment ago, the route to / used the component called App. This is what I wrap everything in to give it access to Material-UI.
First, we'll need to install Material-UI from npm with a quick npm install material-ui. Then we'll start using it.
Pared down a bit, my App component looks like this:
`/resources/assets/js/components/app/app.jsx':
/**
* Controller component
*
* Controls the state for the whole app at the top.
*/
// Import material-ui
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import AppBar from 'material-ui/AppBar';
///////////////////
// Make the app! //
///////////////////
class App extends React.Component {
render() {
return (
<MuiThemeProvider muiTheme={this.props.muiTheme}>
<div>
<AppBar
title="Title"
/>
<div className="content">
{this.props.children}
</div>
</div>
</MuiThemeProvider>
);
}
};
//////////////////////////////
// Set the static variables //
//////////////////////////////
App.propTypes = {};
// Replaces getDefaultProps()
App.defaultProps = {
muiTheme: getMuiTheme()
};
export default App;
I'm using <MuiThemeProvider> here in case I want to modify the theme later (which I probably will). Everything passed down to this.props.children will also be wrapped in that <MuiThemeProvider> to get themed properly as well.
Now when I go to /, I'll get my Home component rendered inside this App component, and will be themed.
That's it for now! You've got yourself a totally useless web app! Yay! Now go make something with it.
What Next?
An excellent question. You'll probably want to learn about Flux and check out some implementations such as Redux (good, free video tutorials over at egghead.io) and Alt, or others (the full story: this article was before Redux was really around, so it didn't get included in the lineup, but I chose Redux myself).
There's probably more, too, but I haven't gotten there myself.
If you have any suggestions on how to improve this little tutorial, let me know. I've been very appreciative of those tutorials I've read, so I'd like to do my best to give back to the community.
Thanks for reading! Let me know what you guys think.



