4 different ways to create Vue Components
Tags: vue
Published: 2019-12-01 16:13:28

Did you know that there are several ways to create Vue Components?
Here we'll discuss 4 of them.

# TL;DR
I've created a *CodeSandbox* project to demonstrate those 4 ways.
If you'd like to self-explore, go ahead and [click here](https://codesandbox.io/s/vue-component-creation-alternatives-cjq44).
If you want the full picture, keep reading.

# The full story
For demonstration purposes, I came up with this simple task that is required from each component that we'll create:

#### Component Requirements
1. Get a list of string items (in the props).
2. Display a button for each item.
 - Each button should have `lightsteelblue` background.
 - The text of each button should be the string of that item.
 - upon clicking a button, the related item is considered *selected*.
3. Display a status string inside a `<p>` element.
 - at first the status should be `Please select your favorite`.
 - when an item is selected the status will change to reflect that selection. For example: if `Picard` is selected, the status should be: `You selected Picard` (yeah, I'm a Trekkie)

I then went ahead and created 4 components:

#### The 4 types of stateful* components
1. SFC (Single File Component)
2. Component with a template
3. Component with a render function
4. Component with a render function and JSX syntax

*There's one more type, a functional component, but it is stateless and therefore cannot help us in this imaginary task

So, let's check out the implementations...

# Implementations
### 1. SFC (Single File Component)
This is the most common one and should be used as default.
The `.vue` file is actually an html file that is being pre-processed by the Vue CLI to allow for special features in the template and in the style tag (e.g. scss and scoped styles)
```vue
<template>
    <div>
        <button v-for="(item, index) in items" :key="index" @click="onItemSelected(item)">{{item}}</button>
        <p v-if="selectedItem">You selected {{selectedItem}}</p>
        <p v-else >Please select your favorite</p>
    </div>
</template>

<script>
    export default {
        name: "ListItemsSfc",
        props: ['items'],
        data() {
            return {
                selectedItem: null
            }
        },
        methods: {
            onItemSelected(item) {
                this.selectedItem = item;
            }
        }
    }
</script>

<style scoped>
    button {
        background: lightsteelblue;
    }
</style>

```
### 2. Component with a template
This is pretty similar to SFC only you don't get the scoped styling and the syntax highlighting (since the template is actually a string)
```javascript
import Vue from 'vue/dist/vue.js'; // <-- important for template components to work

Vue.component('ListItemsTemplate', {
    props: ['items'],
    data() {
        return {
            selectedItem: null
        }
    },
    methods: {
        onItemSelected(item) {
            this.selectedItem = item;
        }
    },
    template: `
                <div>
                    <button style="background: lightsteelblue" v-for="(item, index) in items" :key="index" @click="onItemSelected(item)">{{item}}</button>
                    <p v-if="selectedItem">You selected {{selectedItem}}</p>
                    <p v-else >Please select your favorite</p>
                </div>

                `
});

```

### 3. Component with a render function
Allows for a more dynamic creation of the component's template.
The render function accepts a `createElement` function that is used for rendering a single html element (here shortened as `ce`).
The `createElement` function receives a tag name, an optional data object and an optional content that can be text or additional html child elements, also created with this function.
```javascript
import Vue from 'vue/dist/vue.js'; // <-- important for template components to work

Vue.component('ListItemsRender', {
    props: ['items'],
    data() {
        return {
            selectedItem: null
        }
    },
    methods: {
        onItemSelected(item) {
            this.selectedItem = item;
        }
    },
    render(ce) {
        let getButtonData = (item) => {
            return {
                style: { background: 'lightsteelblue' },
                on: { click: () => this.onItemSelected(item) }
            }
        };
        let buttons = this.items.map(item => ce('button', getButtonData(item), item));
        let statusText = this.selectedItem ? `You selected ${this.selectedItem}` :
            'Please select your favorite';

        return ce('div', [...buttons, ce('p',statusText)]);
    }
});
```
### 4. Component with a render function and JSX syntax
It makes writing complex templates easier.
```jsx
import Vue from 'vue/dist/vue.js'; // <-- important for template components to work

Vue.component('ListItemsJsx', {
    props: ['items'],
    data() {
        return {
            selectedItem: null
        }
    },
    methods: {
        onItemSelected(item) {
            this.selectedItem = item;
        }
    },
    render(h){

        let buttons = this.items.map(item => <button onClick={() => this.onItemSelected(item)} style="background:lightsteelblue">{item}</button>);
        let statusText = this.selectedItem ? `You selected ${this.selectedItem}` :
            'Please select your favorite';

        return (
            <div>
                {buttons}
                <p>{statusText}</p>
            </div>
        );
    }
});
```

And here's the final result, hosted on CodeSandbox. You can open the project and play with the components to test them out.

# The final result
{% codesandbox cjq44 %}

Let me know in the comments in case you have any general questions or any questions related specifically to one of the alternatives presented here.


*Coded with love,*
*Lilo*