Writing Vue.js Render Functions in JSX

Joshua Bemenderfer

As people from the React world are no-doubt familiar with, render functions are a powerful way to write components for your apps. However, they also come with a hefty price-tag when it comes to readability. Facebook’s solution to this is JSX. JSX is a sort of hybrid of JavaScript and XML. It allows you to write XML-style expressions that compile down to virtual DOM objects in order to maintain a semi-consistent visual structure for your component markup. Thankfully, Vue suppoorts JSX too, allowing render functions to be written considerably more clearly.

Installation

The quickest way to get up and running with Vue and JSX is to use the vue-cli template provided by egoist, vuepack.

If you have not already, install vue-cli with

$ npm install -g vue-cli

Then, use vue-cli to create a scaffold with the vuepack template.

$ vue init egoist/vuepack my-project-name
$ cd my-project-name
$ npm install

Manual

Setting up the entire build step for a full Vue app with JSX it outside the scope of this article, but you’ll at least want babel, configured to use either babel-plugin-transform-vue-jsx or the more full-featured babel-preset-vue-app.

If you’re using eslint, you’ll also want to enable jsx and experimentalObjectRestSpread (For JSX spread).

.eslintrc

{
  parserOptions: {
    ecmaVersion: 6,
    ecmaFeatures: {
      experimentalObjectRestSpread: true,
      jsx: true
    },
    sourceType: 'module'
  },
  ...
}

Creating a Component

JSX / render function components can interoperate with template components just fine. After all, Vue’s template language and JSX both compile down to render functions in the end.

However, since JSX is much lower-level than the template language, most of the caveats regarding shorthand directives and such still apply.

An important thing to remember is that JSX maps essentially one-to-one with render functions. It’s just a fancy way of creating a JS object tree, as such, anything you’d do with objects you can do with JSX.

As an exercise, let’s create a basic component, the same one we made in the introduction to render functions article.

ExampleComponent.jsx

export default {
  data() {
    return {
      isRed: true
    }
  },

  /*
   * Same as
   * <template>
   *   <div :class="{'is-red': isRed}">
   *     <p>Example Text</p>
   *   </div>
   * </template>
   */
  render (h) {
    return (
      <div class={{'is-red': this.isRed}}>
        <p>Example Text</p>
      </div>
    )
  }
}

Looks fairly similar to the template example doesn’t it?

JSX Basics

JSX adds a few things to the basic Javascript and XML syntaxes (syntaxi? syntagma? syntaces?) you’re probably familiar with, and they’re a bit different than Vue templates.

Expression binding

Binding TypeVue TemplatesJSX
Stringsprop="attr"prop="attr" (The same)
Expressions:prop="binding"prop={binding}
Objects in bindings:prop="{key: value}"prop={{key: value}} (For clarity)
Event bindings@event="handler($event)", v-on:event="handler"onEvent="handler" (Arguments can be supplied by way of func.bind())
HTML Interpolationv-html="htmlContent"domPropsInnerHTML={htmlContent} (Non-attribute DOM properties have a domProps prefix)
Expression Interpolation{{object}}{object} (Can be nested, like so: <ul>{list.map(item => <li>{item}</li>)}</ul>)

Component references

In JSX, you can refer to imported / in-scope components directly without need for the components: {} object.

ChildComponent.jsx

export default {
  render (h) {
    return (
      <p>I appear to be a child.</p>
    )
  }
}

ExampleComponent.jsx

import ChildComponent from './ChildComponent.jsx'

export default {
  render (h) {
    return (
      <div>
        <p>Example Text</p>
        <ChildComponent/>
      </div>
    )
  }
}

However, lowercased/kebab-cased component references will be looked up in the components: {} object and global component registry, in the same fashion as Vue’s template language.

Functional template composition

A common pattern in JSX land is to split parts of components into separate functions. For example, you might create the virtual DOM for a list in a separate function from the main render function, as shown below. This can be used to great effect for control flow and isolation between the various parts of your component, when you don’t need to create further full-blown components, but want the isolation and testability of pure functions.

ExampleComponent.jsx


function renderUnhealthyDrinkList (h, list) {
  return (
    <ul>
      {list.map(item => <li>{item}</li>)}
    </ul>
  )
}

export default {
  data() {
    return {
      unhealthyDrinks: [
        'Coffee',
        'All the sodas',
        'That stuff under the bag in the trashcan'
        // Hot chocolate is perfectly okay.
      ]
    }
  },

  render (h) {
    return (
      <div>
        {renderUnhealthyDrinkList(h, this.unhealthyDrinks)}
      </div>
    )
  }
}
✖ Clear

🕵 Search Results

🔎 Searching...