This tutorial is out of date and no longer maintained.
Vue.js has an easy API and several options for defining HTML templates in our components.
We can use the <template>
tag option, define a template
property on our root component instance, or use Single-File components.
The options above are awesome and work perfectly, but, there comes a time in the lifecycle of your application where they either feel clunky, over-engineered or very inflexible.
So, why would we want to JSX instead of any of the other template definitions?
<div>...</div>
is subjectively better than this.$createElement('div', {}, [...])
Let me give you an example of why JSX is good.
We want to build a <TextField/>
component that can either be a normal single-line text input or a multiline input (textarea). Our template declaration might look like this.
<div>
<textarea v-if="multiline" v-model="content" :name="name" :placeholder="placeholder" :aria-invalid="false">
<input v-else v-model="content" :name="name" :placeholder="placeholder" :aria-invalid="false">
</div>
As you can see from the snippet above, we’ll quickly run into a few problems like duplicate code and many more. Imagine having to support a variety of properties on the input. This little snippet above will grow and be a nightmare to maintain.
To fix this, we need to go low-level with Vue. We need to get closer to Vue’s internal API to fix this mess.
render()
methodNote: I’m not saying that there’s not a simple way or ways to handle the above problem without JSX, all I’m saying is that moving this logic to the render()
method with JSX in tow can make for a more intuitive component. Keep reading to find out why?
Every component we create in Vue has a render method. This is where Vue chooses to render the component. Even if we don’t define this method, Vue will do it for us.
This means that when we define HTML templates in Vue — Vue’s template compiler compiles it to a createElement
function that takes a couple of parameters and returns the result from the render
function.
To fix the code in the previous section, we remove the template
property or the template tag and define a render()
method on the component. If the render
method is defined on a component, Vue will ignore the template definition.
...
export default {
name: 'TextField',
render (createElement) {
const tag = this.multiline ? 'textarea' : 'input'
return createElement(tag, {
class: {
'text-input': true,
'is-disabled': false
},
attrs: {
name: this.name,
placeholder: this.placeholder,
'aria-invalid': false
}
})
}
}
...
The above code does a few things:
render
method takes a createElement
helper from Vue.createElement
.Note: Every template we define for a Vue component will be converted into a render
method that returns a createElement
function. It’s because of this reason the render
method will take precedence over a template definition.
Take this example:
<div>
<p>Only you can stop forest fires</p>
</div>
The template compiler will convert the HTML above into:
...
render (createElement) {
return createElement(
'div',
{},
createElement(
'p',
{},
'Only you can stop forest fires'
)
)
}
...
Okay! now you might ask this question, “Isn’t this bad for readability?” The answer is yes. Once you define a component with many levels of elements nesting or has several sibling elements — we run into a new problem. We just sacrificed readability. Like they say, “we’ve moved from the frying pan to fire.”
Cue JSX. This is where we’ll have JSX bring back the readability we lost.
If you already know about JSX, feel free to skip to the next section where I’ll show you how to use JSX in Vue.
JSX is a term coined by Facebook’s engineering team.
JSX is an XML-like syntax extension to JavaScript without any defined semantics.
JSX is NOT intended to be implemented by engines or browsers. Instead, we’ll use transpilers like Babel to convert JSX to regular JavaScript.
// this line below is an example of JSX
const heading = <h1>Welcome to Scotch</h1>;
Basically, JSX lets us use an HTML-like syntax in JavaScript.
Unfortunately, this article assumes you already know JSX, so teaching JSX is beyond the scope of this article. I’ll still point you in the right direction. JSX is very easy to grok and can be done in a couple of minutes.
Use these links to learn The basics of JSX, Learn JSX in-depth, finally, if you really want to know about the specification that is JSX, visit its official website.
If you use Vue-cli greater or equal to version 3.0 you are in luck as JSX is supported.
If you are using an older version of Vue-CLI that doesn’t support JSX, you can add it by installing babel-preset-vue-app
and add it to your .babelrc
file.
To install using npm:
- npm install --save-dev babel-preset-vue-app
Using yarn:
- yarn add --dev babel-preset-vue-app
In your .babelrc
file, all you have to do is:
{
"presets": ["vue-app"]
}
There, we can now use JSX in our component’s render
function.
There are few gotchas to using JSX in Vue.
First, you can no longer use the :
and @
shortcuts for binding and listening to events. They are invalid JSX syntax and your code won’t compile.
To listen for events in JSX, we need the “on” prefix. For example, use onClick
for click events.
render (createElement) {
return (
<button onClick={this.handleClick}></button>
)
}
To modify events, use:
render (createElement) {
return (
<button onClick:prevent={this.handleClick}></button>
)
}
To bind a variable, instead of :
use:
render (createElement) {
return (
<button content={this.generatedText}></button>
)
}
To set HTML string as the content of an element, instead of v-html
use:
render (createElement) {
return (
<button domPropsInnerHTML={htmlContent}></button>
)
}
We can also spread a large object.
render (createElement) {
return (
<button {...this.largeProps}></button>
)
}
render
Going back to our initial “TextField” component. Now that we have JSX enabled in our Vue app, we can now do this.
render (createElement) {
const inputAttributes = {
class: 'input-field has-outline', // class definition
onClick: this.handleClick // event handler
backdrop: false // custom prop
}
const inputMarkup = this.multiline
? <textarea {...inputAttributes}></textarea>
: <input {...inputAttributes}/>
return inputMarkup
}
Another benefit to using JSX in Vue is that we no longer have to register every component we need. We just import and use.
import {Button} from '../components'
export default {
render (createElement) {
return <Button primary={true}>Edit</Button>
}
}
TypeScript is used as a mechanism that adds type-checking to JavaScript.
To add JSX support to TypeScript all we need to do is modify our tsconfig.json
.
To enable JSX in TypeScript, first save the file as a .tsx
file and modify your tsconfig.json
to include:
{
"compilerOptions": {
...
"jsx": "preserve",
}
}
Setting the jsx
option to “preserve” means that TypeScript should not process the JSX. Doing this lets Babel take control of everything JSX and TypeScript stick to types as it does not yet support Vue JSX. You can learn more.
Then create a jsx.d.ts
file in your project and add the TypeScript JSX declarations for Vue.
import Vue, {VNode} from 'vue'
declare global {
namespace JSX {
interface Element extends VNode {}
interface ElementClass extends Vue {}
interface ElementAttributesProperty {
$props: {}
}
interface IntrinsicElements {
[elemName: string]: any
}
}
}
Make sure that TypeScript can load the declaration file. Or, you can add autoloading for it in tsconfig.json
via:
{
"compilerOptions": {
...
"typesRoot": ["./node_modules/@types", "./types"]
}
}
That’s it for today. Enjoy having some or all of your Vue.js templates in JSX.
And, please no complaints about JSX breaking SOC (separation of concerns), I can’t take another one of those arguments. If you prefer using the createElement
function with objects by all means enjoy!!
Let me know your thoughts and suggestions in the comments.
Cheers!
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
Join our DigitalOcean community of over a million developers for free! Get help and share knowledge in our Questions & Answers section, find tutorials and tools that will help you grow as a developer and scale your project or business, and subscribe to topics of interest.
Sign up nowThis textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!