Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Single File Component

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.

As we've seen, it's totally possible to configure the Vue instance and put the template in the same file. But... this is going to get crazy as our app grows: can you imagine writing 100 lines of HTML inside this string... or more? Yikes! Fortunately, Vue solves in a unique, and pretty cool way: with single file components.

Inside the js/ directory create a new folder called pages/, and then a file called products.vue. We'll talk more about the directory structure we're creating along the way.

Notice that .vue extension: these files aren't really JavaScript, they're a custom format invented by Vue.

Creating the Single File Component

On top, add a <template> tag. Then, copy the h1 HTML from the original file, delete the template variable, and paste here.

<h1>Hello {{ firstName }}! Is this cooler?</h1>
... lines 4 - 14

Next, add a <script> tag. Anything in here is JavaScript and we'll export default an object that will hold our Vue options. Copy the data() function, delete it, and move it here.

... lines 1 - 4
export default {
data() {
return {
firstName: 'Ryan',

That's it! I know, the format is a bit strange, but it's super nice to work with. On top, the <template> section allows us to write HTML just like if we were in a Twig template. And below, the <script> tag allows us to set up our data, as well as any of the other options that we'll learn about. This is a fully-functional Vue component.

Using the Single File Component

Back in products.js, to use this, first, import it: import App - we could call that variable anything - from './pages/products. Thanks to Encore, we don't need to include the .vue extension.

... line 1
import App from './pages/products';
... lines 3 - 10

Now, inside of render, instead of worrying about compiling the template and all this boring, crazy-looking code, the App variable already has everything we need. Render it with return h(App).

... lines 1 - 3
new Vue({
el: '#app',
render(h) {
return h(App);


For Vue 3, the code should look like this:

import { createApp } from 'vue';
import App from './pages/products';


That feels good! Let's try it: move over, refresh and... it still works!

Adding a Component Name

From here on out, we're going to do pretty much all our work inside of these .vue files - called single file components. One option that we're going to add to every component is name: set it to Products. We could use any name here: the purpose of this option is to help debugging: if we have an error, Vue will tell us that it came from the Products component. So, always include it, but it doesn't change how our app works.

... lines 1 - 5
export default {
name: 'Products',
... lines 8 - 12
... lines 14 - 15

$mount() the Component

Before we keep working, there are two small changes I want to make to products.js. First, the el option: it tells Vue that it should render into the id="app" element on the page. This works, but you usually see this done in a different way. Remove el and, after the Vue object is created, call .$mount() and pass it #app.

... lines 1 - 3
new Vue({
render(h) {
return h(App);

I also like this better: we first create this Vue object - which is a template and set of data that's ready to go - and then choose where to mount it on the page.

Shorthand render() Method

Second, because the render() method only contains a return line, we can shorten it: render set to h => h(App).

... lines 1 - 3
new Vue({
render: (h) => h(App),

That's effectively the same: it uses the arrow function to say that render is a function that accepts an h argument and will return h(App). I'm mostly making this change because this is how you'll see Vue apps instantiated on the web.

Next, let's get to work inside our single file component: we'll add the HTML markup needed for our product list page and then learn how we can add styles.

Leave a comment!

This course is also built to work with Vue 3!

What JavaScript libraries does this tutorial use?

// package.json
    "devDependencies": {
        "@symfony/webpack-encore": "^0.30.0", // 0.30.2
        "axios": "^0.19.2", // 0.19.2
        "bootstrap": "^4.4.1", // 4.5.0
        "core-js": "^3.0.0", // 3.6.5
        "eslint": "^6.7.2", // 6.8.0
        "eslint-config-airbnb-base": "^14.0.0", // 14.1.0
        "eslint-plugin-import": "^2.19.1", // 2.20.2
        "eslint-plugin-vue": "^6.0.1", // 6.2.2
        "regenerator-runtime": "^0.13.2", // 0.13.5
        "sass": "^1.29.0", // 1.29.0
        "sass-loader": "^8.0.0", // 8.0.2
        "vue": "^2.6.11", // 2.6.11
        "vue-loader": "^15.9.1", // 15.9.2
        "vue-template-compiler": "^2.6.11", // 2.6.11
        "webpack-notifier": "^1.6.0" // 1.8.0