Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This course is archived!
While the concepts of this course are still largely applicable, it's built using an older version of Symfony (4) and React (16).


Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

Time to install React! Find your open terminal: yarn add react - and we also need a second package called react-dom:

yarn add react react-dom --dev

react is... um... React. react-dom is the library we'll use to render our React app onto the page, the DOM. These are separate libraries because you can actually use React to render things in a non-browser environment.

Anyways, when that finishes, go back to rep_log_react.js. Like with all libraries, to use React, we need to require or import it. I'll use the newer, far more hipster import syntax in this tutorial to import React from react.

Elements & the Virtual DOM

Here's the idea behind React: it's actually really beautiful: we create React element objects that represent HTML elements, like an h1 tag. These don't actually live on the page, they're just objects that describe HTML elements.

But then, we can tell React to look at our element objects, and use them to create real HTML elements on the page, or, on the DOM. This means that we will have a tree of element objects in React and a tree of element objects on the page. To say it a different way, we will have a virtual DOM in React and the real DOM on the page.

And... that's it! Of course, the magic is that, when we change some data on a React element object, React will update the corresponding DOM element automatically.

Creating React Elements

So... let's create some React elements! To really master this, yea... we're going to do things the hard way first. But, it will be totally worth it.

Create a new const el set to React.createElement(). Make this an h2 tag: we're building the title on top of the app. Pass null for the second argument: but this is where you could pass an array of any HTML attributes for the element. These are called "props" in React - but more on that later. For the third argument, pass whatever you want to put inside, like text: "Lift History!".

import React from 'react';
const el = React.createElement('h2', null, 'Lift History!');
... lines 4 - 5

Cool! Let's console.log(el): I want you to see that this is just a simple object. Go refresh the page. The element is not, yet, being rendered to the screen in any way. It's just a React element that describes a potential HTML element.

... lines 1 - 3

Rendering React to the DOM

Now, go back to index.html.twig. To render the element onto the page, we need a target. The existing app is rendered in this js-rep-log-table element. Above this, add a new <div class="row"> and inside, an empty div we can render into with id="", how about, lift-stuff-app. Then, for a bit more separation, add a bunch of line breaks and an <hr>.

... lines 1 - 2
{% block body %}
<div class="row">
<div id="lift-stuff-app"></div>
<hr />
... lines 11 - 55
{% endblock %}
... lines 57 - 70

Awesome! Copy the id of the div. To render React to the DOM, we need to use that other package we installed: import ReactDom from react-dom. Then, just, ReactDom.render() to render our el into document.getElementById('lift-stuff-app').


In React 18, this part is a bit different. Use this instead:

// A) the import is slightly different
import { createRoot } from 'react-dom/client';

// B) exactly the same as before
const el = React.createElement('h2', null, 'Lift History!');

// C) to render, you now create a "root" element
//    then render into it
const root = createRoot(document.getElementById('lift-stuff-app'));

... line 1
import ReactDom from 'react-dom';
... lines 3 - 5
ReactDom.render(el, document.getElementById('lift-stuff-app'));

That's it! Step 1: create a React element object and, step 2, use ReactDom and some boring, raw JavaScript to render it onto the page.

Let's go try it! Move over and refresh! Ha! We have our very first, but I, know very simple, React app. We deserve balloons!

Nested Elements

Of course, in a real app, we're going to have more than just one element. Heck, we're going to have a big nested tree of elements inside of other elements, just like we do in normal HTML.

So... how could we put an element inside of our h2? First, break things onto multiple lines to keep our sanity. The answer is... by adding more and more arguments to the end of React.createElement(). Each argument - starting with the third argument - becomes a new child that lives inside the h2. For example, to create a nested span element, use React.createElement() with span, null and a heart Emoji.

... lines 1 - 3
const el = React.createElement(
'Lift History! ',
React.createElement('span', null, '❤️')
... lines 12 - 13

Let's log el again. Then, flip over and... refresh!

Ha! There it is! Inspect the element: yep, the h2 tag with a span inside. Check out the logged Element: it now has two "children", which is a React term: the text and another React element object.

Awesome! But... you've probably already noticed a problem. Building a real app with many nested elements is going to get really ugly... really quickly. This React "element" idea is great in theory.... but in practice, it's a nightmare! We need another tool to save us. That tool is love. I mean, JSX.

Leave a comment!

Login or Register to join the conversation
xmontero Avatar
xmontero Avatar xmontero | posted 9 months ago | edited

Hi, I just bought this amazing course. Congrats Ryan and Frank.

As time elapses, things evolve, I find 2 things that could be worth nothing in "cards" in the videos one for video 1 and the other for this video. I have already posted a separate comment for video 1.

For this video... It happens 2 things:

a) Autocompletion.

After doing the yarn add react react-dom --dev when in PHP Storm, writing import Rea suggests React and finally PhpStorm completes the full statement: import React from "react";

Nevertheless when writing import ReactD does not suggest ReactDom which triggers a thinking about "maybe React and ReactDom are behaving differently and the video treats them as equal in significance from the static analysis point of view.

IDK if this is is related to the next...

b) Warning when executing:

After executing it, the console says

Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot

I wonder... Should the video tell that after React version 18 things are different? How should I proceed?


Hey again xmontero!

Hmm, yes, my guess is that these are both related! I just added a note about how to run the code in React 18 - let me know if you hit any issues with it. The note is in the script, but here it is again:

// A) the import is slightly different
import { createRoot } from 'react-dom/client';

// B) exactly the same as before
const el = React.createElement('h2', null, 'Lift History!');

// C) to render, you now create a "root" element
//    then render into it
const root = createRoot(document.getElementById('lift-stuff-app'));

We'll add a video note for that as well.


1 Reply
xmontero Avatar

As stated, it works! Thnx!


Hello guys,

In my phpstorm, the parameter hint box show many lines as opposed to the single line shown in the video. Do you know why, what is it displaying and how to use it?

Here is a screen snip:

Thank you for the work you do and all these wonderful tutorials.


Hey Skylar

Try changing your JS language version to "React JSX" inside settings -> Languages & Frameworks -> Jasvascript



MolloKhan Thank for the reply, but it was already set to "React JSX"


Hmm, have you tried asking it on PHPStorm site comments? I think PHPStorm autocomplete JS based on libraries you have installed but I'm not sure how you can adapt it to your needs

Authoritas Avatar
Authoritas Avatar Authoritas | posted 5 years ago

Why would use --dev when adding react and react-dom? Surely they'll both be needed in production?


Hey Authoritas!

Great question! This is a confusing detail... that's actually not important at all. In fact, react and react-dom will NOT be used on production. Well, to be more accurate, we do not need the node_modules/react and node_modules/react-dom directories to be available on production. That's because, during deploy, we will have used Webpack to pack them into some final JavaScript files. Ultimately, on production, we don't even need node installed, or any of the packages to be installed.

Of course, if you're like me, 100% of your Node packages are being used in this way. But, in theory, someone could build an app where they have a combination of things like react & react-dom that are "processed" by Webpack AND a few other node libraries that truly *are* being used at runtime, in the production server. So, that's why I like to keep the react, webpack, etc stuff in require-dev :).


Authoritas Avatar

Thanks very much. I kind of figured that might have been the case. That makes sense. It's all beginning to click!! 🙂Presumably, if you do use yarn add {package_name} without the --dev flag accidentally and just yarn remove {package_name} and then add it back in with the --dev (yarn add {package_name} --dev), all this is doing is juggling the package's key/value pair in your package.json file deleting the required directories in ./node-modules and then adding them back. You could, of course, just do this manually by simply editing your package.json directly?


Hey Authoritas

> You could, of course, just do this manually by simply editing your package.json directly?

You're totally correct, you can just do it manually and everything will work fine


How did you get to choose from a popup selection of emojis in Phpstorm ?


Hey Skylar!

Ha! I see you are paying attention to the important details ;). Actually, this is an OSX shortcut - not from PhpStorm. It's control+command+spacebar on OSX to bring up that menu anywhere. I... kinda use it all the time.



Well, I found an answer so let me share. Microsoft has added a dedicated Emoji Panel or Picker to Windows 10 v 1709. Press Windows Key + Period (.) or Windows Key + semicolon (;) to bring up Emoji Panel. https://www.thewindowsclub....


That's great! Thanks for sharing it :)

Default user avatar
Default user avatar Amine Tzouk | posted 5 years ago

I hope we will have the next tutorials soon.

best regards.


Hey Amine Tzouk!

We'll release a new video every day - so very soon! :)

Cat in space

"Houston: no signs of life"
Start the conversation!

While the concepts of this course are still largely applicable, it's built using an older version of Symfony (4) and React (16).

What PHP libraries does this tutorial use?

// composer.json
    "require": {
        "php": "^7.2.0",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "doctrine/doctrine-bundle": "^1.6", // 1.9.1
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.3
        "doctrine/doctrine-fixtures-bundle": "~3.0", // 3.0.2
        "doctrine/doctrine-migrations-bundle": "^1.2", // v1.3.1
        "doctrine/orm": "^2.5", // v2.7.2
        "friendsofsymfony/jsrouting-bundle": "^2.2", // 2.2.0
        "friendsofsymfony/user-bundle": "dev-master#4125505ba6eba82ddf944378a3d636081c06da0c", // dev-master
        "sensio/framework-extra-bundle": "^5.1", // v5.2.0
        "symfony/asset": "^4.0", // v4.1.4
        "symfony/console": "^4.0", // v4.1.4
        "symfony/flex": "^1.0", // v1.17.6
        "symfony/form": "^4.0", // v4.1.4
        "symfony/framework-bundle": "^4.0", // v4.1.4
        "symfony/lts": "^4@dev", // dev-master
        "symfony/monolog-bundle": "^3.1", // v3.3.0
        "symfony/polyfill-apcu": "^1.0", // v1.9.0
        "symfony/serializer-pack": "^1.0", // v1.0.1
        "symfony/swiftmailer-bundle": "^3.1", // v3.2.3
        "symfony/twig-bundle": "^4.0", // v4.1.4
        "symfony/validator": "^4.0", // v4.1.4
        "symfony/yaml": "^4.0", // v4.1.4
        "twig/twig": "2.10.*" // v2.10.0
    "require-dev": {
        "symfony/debug-pack": "^1.0", // v1.0.6
        "symfony/dotenv": "^4.0", // v4.1.4
        "symfony/maker-bundle": "^1.5", // v1.5.0
        "symfony/phpunit-bridge": "^4.0", // v4.1.4
        "symfony/web-server-bundle": "^4.0" // v4.1.4

What JavaScript libraries does this tutorial use?

// package.json
    "dependencies": {
        "@babel/plugin-proposal-object-rest-spread": "^7.12.1" // 7.12.1
    "devDependencies": {
        "@babel/preset-react": "^7.0.0", // 7.12.5
        "@symfony/webpack-encore": "^0.26.0", // 0.26.0
        "babel-plugin-transform-object-rest-spread": "^6.26.0", // 6.26.0
        "babel-plugin-transform-react-remove-prop-types": "^0.4.13", // 0.4.13
        "bootstrap": "3", // 3.3.7
        "copy-webpack-plugin": "^4.4.1", // 4.5.1
        "core-js": "2", // 1.2.7
        "eslint": "^4.19.1", // 4.19.1
        "eslint-plugin-react": "^7.8.2", // 7.8.2
        "font-awesome": "4", // 4.7.0
        "jquery": "^3.3.1", // 3.3.1
        "promise-polyfill": "^8.0.0", // 8.0.0
        "prop-types": "^15.6.1", // 15.6.1
        "react": "^16.3.2", // 16.4.0
        "react-dom": "^16.3.2", // 16.4.0
        "sass": "^1.29.0", // 1.29.0
        "sass-loader": "^7.0.0", // 7.3.1
        "sweetalert2": "^7.11.0", // 7.22.0
        "uuid": "^3.2.1", // 3.4.0
        "webpack-notifier": "^1.5.1", // 1.6.0
        "whatwg-fetch": "^2.0.4" // 2.0.4