One of the most powerful patterns in programming is the idea of higher-order functions: functions which can take other functions as arguments or return them as their return values. If you’ve spent much time at all working in JavaScript, you’ve certainly encountered these—whether you’re using Array.map to transform the values in an array, or passing a function as an argument to an event handler.

The same pattern is incredibly useful in building components, and most modern front-end frameworks support it—including Ember.js! (In React, the pattern as a whole is often known as the renderProps pattern, for the way you most often accomplish it. It’s all the same idea, though!)

In this little post, I’ll show you how to build a small “higher-order component” in Ember.js, hopefully demystifying that term a little bit a long the way. (If you just want to see how the pieces fit together, you can see the finished app in this repo.)

I’m going to be using classes and decorators throughout. Both are very much ready-to-go in Ember, and I commend them to you! I’m also going to be using some of the new optional features available in Ember 3.1+ to use template-only components! Note that one of the most important consequences of this is that arguments have to be referenced as @theArgumentName rather than just theArgumentName in templates. The reason is precisely that there is no backing JavaScript component. In old-school Ember.js components, {{theArgumentName}} is implicitly turned into {{this.argumentName}} , which does a lookup on the backing component. In Glimmer-style components—of which these are the first part—arguments live on a designated args property and are accessible in templates via @theArgumentName instead.

Higher-Order Components, What Are They Just like with a “higher-order function,” all we mean when we talk about a “higher-order component” is a component which takes other components as arguments, returns other components itself (in Ember’s case via yield in a template), or both. The thing we’re actually going to build here is a “modal” which accepts an optional button as an arguments, and which yields out a component for dividing the modal into sections visually so you can pass your own content in and have it look just right. This is closely based on a component my colleagues and I at Olo built recently, just with some of our specific details stripped away to get at the actually important bits. Here’s what it looks like in practice: a modal with sectioned text and a close button The goal for the button arguments is to let the modal be able to render the button the caller passes in, while not being concerned with the functionality of the button. Otherwise, we’d have to tie the “API” of the modal to the details of button behavior, bind more actions into it, etc. The goal for the yielded sectioning component is for whatever is rendering the modal itself to be able to pass content in and get it chunked up however the modal decides is appropriate—the modal can display its own styles, etc.—without having to worry about the details of applying classes or sectioning up the content itself. In short, we want to separate our concerns: the modal knows how to lay out its contents and where to put buttons, but it doesn’t want to have to know anything about what the buttons do. The most complicated interaction in the world could be going on, and the modal won’t have to care. Likewise, things using the modal can pass content and buttons into it, and let the modal manage its own layout and so on without having to be concerned with the details of that. So what does that look like in practice? The approach I use here builds on the “contextual components” pattern in Ember.js. The main new idea is that the context includes components!

Implementing It We have three components here: a button

a modal

a modal section Since Ember.js still (for now!) requires component names to be at least two words separated by a dash, we’ll just call these x-button , x-modal , and x-modal-section . x-button The button component, we’ll keep pretty simple: it’s just a button element with a given label and an action bound to it: <button class={{@buttonClass}} type='button' {{action @onClick}}> {{@label}} </button> x-modal The x-modal has the meat of the implementation. <div class='modal-backdrop'></div> <div class='modal'> <div class='modal-content'> {{yield (hash section=(component 'x-modal-section'))}} </div> {{#if @button}} {{component @button buttonClass='modal-button'}} {{/if}} </div> The two things two notice here are the yield and the component . The yield statement yields a hash with one property: section . Yielding a hash is a convenient pattern in general. Here, we’re doing it to make the API nicer for users of this component. It means that if we name the yielded value |modal| when we invoke this, we’ll be able to write modal.section to name this particular yielded item. (You’ll see exactly this below.) We use the component helper twice: once as the value of the section key in the yielded hash, and once for the button below. In both cases, the helper does the same thing: invokes a component! While the most common way to render a component is with its name, inline—like {{x-modal}} —you can always render it with the component helper and the name as a string: {{component 'x-modal'}} . This lets you render different components dynamically! Let’s remember our initial analogy: the same way you can pass different functions to a higher-order function like Array.prototype.map , you can pass different components to a higher-order component like our x-modal here. And just like you can return a function from a higher-order function, we can yield a component from a higher-order component. Just like higher-order functions, the function passed in or returned just has to have the right shape. For example, the argument to Array.prototype.map needs to be a function which performs an operation on a single item in the array (and maybe also the index) and hands back the result of that operation. Similarly, the button argument to our x-modal needs to accept a buttonClass component so that the modal can apply some styling to it. The same thing holds for the component being yielded back out: it has an API you should use to invoke it, just like any other. All of this gets at something really important: you can think of components as just being pure functions: they take some input in the form of arguments, and give you the output of what they render and what they yield—and they always give you the same rendered HTML and the same yielded values for the same inputs. They’re just functions! x-modal-section The x-modal-section component is the simplest of all of these: it has no behavior, just some styling to actually chunk up the content: <div class='modal-section'> {{yield}} </div> Application controller and template Now, let’s use in the context of the application template, where we can see how the pieces all fit together. First, let’s see the application controller backing it—nothing unusual here, just a simple toggle to show or hide the modal. import Controller from "@ember/controller"; import { action } from "@ember-decorators/object"; export default class Application extends Controller { constructor() { super(...arguments); this.showModal = false; } @action showIt() { this.set("showModal", true); } @action hideIt() { this.set("showModal", false); } } Now for the interesting bit—the template where we invoke x-modal and use its higher-order-component functionality: {{#if showModal}} {{#x-modal button=(component 'x-button' label='Close modal!' onClick=(action 'hideIt') ) as |modal| }} {{#modal.section}} Here is some content! {{/modal.section}} {{#modal.section}} Here is some other content. {{/modal.section}} {{#modal.section}} <p>The content can have its own sections, as you'd expect!</p> <p>Nothing crazy going on here. Just a normal template!</p> {{/modal.section}} {{/x-modal}} {{/if}} <button class='button' {{action 'showIt'}}>Show modal</button> <!-- some other content on the page --> We invoke the block form of x-modal just like we would any block component, and we get back the thing it yields with as |modal| . However, one of the arguments we pass to it is a component. But modal is a hash (an object!) with a property named section , which is the x-modal-section component. Again, you can think of this like calling a function with one function as an argument and getting another function back as its return value—that returned function being something we could call over and over again once we had it. Here, we “call the function”—invoke the x-modal component—with component 'x-button' as its argument, and the returned modal.section is a component we can invoke like a normal component. We could even pass it into some other component itself if we so desired. And that’s really all there is to it!