Hey Reactionaries! I don't think this topic has been covered in depth yet on the blog or in our Docs. In the last week of our Introduction to Reaction's Architecture series, we're taking a good look at plugin architecture.
We're going to dive in super deep on the register.js
file, which each and every Reaction plugin requires. My hope is that once you've finished reading this post, you'll have a solid foundational understanding of how to create your own Reaction register.js
file for plugins from scratch. You'll also have a good library of examples to look at when you get stuck.
When you write your registry file, you will call the Reaction.registerPackage
method in a file that lives in /imports/plugins
. I'll show you a few examples where we use reactionApps
to refer to a plugin. In general, remember that Reaction package = plugin = reactionApp. I'll generally refer to them as plugins in this post.
The Registry is the defining file of any Reaction plugin. You can think of the register.js
file as similar, but not identical to, the package.json
file you'd use to define an npm package. There are plenty of examples to refer to in our GitHub repo, since almost all of our core functionality has been written as plugins. We have two directories of core plugins, core
and included
.
Here's a gutted example of a register.js file:
Reaction.registerPackage({
label: "PackageName",
name: "reaction-example-package",
icon: "fa fa-package",
autoEnable: true,
settings: {
name: "Marketplace",
enabled: true,
public: {
somePublicSetting: true
}
},
registry: [] // Array of registry objects - optional
layout: [] // Array of layout objects - optional
});
Once registered, plugins are published to the client in the Packages publication. Inspecting this publication will give you some insight as to how and what parts of plugins are published to different parts of the app and how plugin settings can be public or private.
I'm breaking this guide down into 3 sections. First, we'll discuss what I'm calling the top-level properties, which consist of the following: label
, name
, icon
, autoEnable
, settings
, registry
, and layout
. After that, we'll look at the architecture of a registry
object. And finally, we'll round off the deep dive by taking a look at the architecture of a layout
object.
Let's get to it.
Top-Level Properties
Name
name: "reaction-dashboard"
The name
is typically used to refer to the plugin internally, and it must be unique. If you're building your own, you should namespace your plugin to avoid conflicts, e.g. yourorg-plugin-name
.
i18n Namespace
The plugin name is also used to determine the i18n namespace for the plugin. You can see how this is used in the reaction-ui
Package en.json file and then implemented in filter.js.
Label
label: "Dashboard"
The label
is displayed wherever the client refers to the plugin with a text label.
Icon
icon: "fa fa-th"
The icon
is a set of classes that are used to define an icon. The example above refers to a Font Awesome icon, but with some extra work, it could include other types of icons as well.
AutoEnable
autoEnable: true
The autoEnable
flag tells the app whether or not the plugin should be enabled on load, or if it must be turned on after the app has been started. Most core plugins have this set to true.
Settings
settings: {
public: {
allowGuestCheckout: true
},
mail: {
user: "",
password: "",
host: "",
port: ""
},
openexchangerates: {
appId: "",
refreshPeriod: "every 1 hour"
},
paymentMethod: {
defaultPaymentMethod: ""
}
}
The settings property is required, but it can also just be an empty object, {}
.
The settings
object permits you to define both public and private settings and defaults for a plugin.
Anything that you put into the public
property inside settings will be published to the client for all users. Everything that is outside of the public property will be private. API keys, passwords and other sensitive information should never be stored inside the public property. You should also never commit code that has any sensitive information to your repo. Any settings you create here can be added to an admin dashboard so that users can insert sensitive information through a secure connection.
These settings can be retrieved with the getPackageSettings method. The settings
object (see also: Reaction Registry Schema) is a blackbox
, which means that the schema will permit anything inside the object. For more info, check out the blackbox docs for meteor-simple-schema.
Registry
registry: [...RegistryObjects],
The registry
array of registry objects is where you can define routes and templates. We'll get into the properties in much more detail below. This setting is optional.
Layout
layout: [...LayoutObjects]
The layout
array of layout objects is where you can define routes and templates. We'll get into all of the properties in much more detail below. This setting is optional.
The Registry Property
The registry property takes an array of objects that use the Registry Schema to define them. Below, you'll find an example from our Core Accounts plugin:
registry: [{
route: "/dashboard/accounts",
name: "accounts",
provides: "dashboard",
label: "Accounts",
description: "Manage how members sign into your shop.",
icon: "fa fa-users",
container: "core",
template: "accountsDashboard",
workflow: "coreAccountsWorkflow",
priority: 1
}, {
route: "/account/profile/verify:email?",
label: "Account Verify",
name: "account/verify",
workflow: "coreAccountsWorkflow",
template: "verifyAccount"
}, {
label: "Account Settings",
icon: "fa fa-sign-in",
provides: "settings",
route: "/dashboard/account/settings",
container: "accounts",
workflow: "coreAccountsWorkflow",
template: "accountsSettings"
}, {
route: "/dashboard/accounts",
name: "dashboard/accounts",
workflow: "coreAccountsWorkflow",
provides: "shortcut",
label: "Accounts",
icon: "fa fa-users",
priority: 1,
container: "dashboard",
template: "accountsDashboard"
}, {
route: "/account/profile",
template: "accountProfile",
name: "account/profile",
label: "Profile",
icon: "fa fa-user",
provides: "userAccountDropdown"
}],
Reading through this real-world example should give you a framework for the different ways you can use the registry for your plugin.
Provides
provides: "dashboard",
People frequently ask about this property, so I'd like to go more in-depth with it. At the core, this property tells us how a part of the app should be used. Different provides
values will have different requirements in terms of the other properties that should be listed alongside them.
The best way to understand exactly how to structure a registry entry that provides a certain service, template, or route is to look at an example of how a core plugin does it. I've provided links to different parts of our code for each valid provides
value. Feel free to ask more in-depth questions, and we can continue to make this document better.
Currently, the following are valid provides
values:
provides: "addressValidation"
- Registers an address validation service to perform address validation. This isn't associated with a route.
provides: "addressValidation"
requires:
For more examples, check out our Avalara integration: Avalara register.js, Avalara Address Validation Method Definition, and Avalara validateAddress method.
provides: "catalogSettings"
- This will register a template to appear in the catalogSettings panel of the dashboard.

provides: "catalogSettings"
requires:
label
name
template
provides
To see an example, check out our Revisions plugin.
provides: "dashboard"
- This will cause a link with an icon and a label to appear in the Action Panel. The link will only be visible for users with appropriate permissions. This value is very similar to provides: "settings"
.

To see where these are inserted, check out the packageListContainer. For an example of how to insert these into your plugins, check out our Accounts plugin.
provides: "dashboard"
requires:
container
(unused)
description
(unused)
icon
label
priority
(unused)
provides
name
route
(unused)
template
workflow
(unused)
provides: "paymentMethod"
- This will register a payment method form template. These payment forms will get rendered at checkout if the payment method is enabled. For examples, check out our Stripe plugin method.
Each enabled payment method is listed in the dropdown to select a default payment method. These are selected from registry entries that provide "paymentMethod" in the payment settings.js file.

provides: "paymentMethod"
requires:
provides: "paymentSettings"
- This will register a template to appear in the paymentSettings panel of the dashboard. See the register.js of our Stripe plugin as a reference.


provides: "paymentSettings"
requires:
provides: "settings"
- This will register a link with an icon and a label to appear in the dashboard settings section. Link will only be visible for users with appropriate permissions. This value is very similar to provides: "dashboard"
.

For examples of where and how to insert these into your plugis, see the pageListContainer and the Shipping register.js.
provides: "settings"
requires:
label
name
description
(unused)
icon
template
provides
provides: "shippingSettings"
- This will register a template to appear in the shippingSettings panel of the dashboard. For an example, check out our Flat Rate Shipping plugin.

provides: "shippingSettings"
requires:
label
template
icon
name
description
provides
provides: "shortcut"
- This adds a link to an admin dropdown menu. When a user image or name is clicked on, a list of shortcuts (which the user has been given permission to access) will appear. shortcut
s and userAccountDropdown
s are fairly similar, except shortcut
s will check for permissions before displaying a link, while userAccountDropdown
s will display the link to all logged-in users.

Shortcuts get passed into mainDropdown.js and then rendered to the DropDownMenu component in the same file. See the Accounts register.js for an example of a shortcut in the registry.
provides: "shortcut"
requires:
route
(unused)
name
workflow
(unused)
label
icon
priority
(unused)
container
(unused)
template
provides
provides: "social"
- This will register a template to appear in the product social template. The only core plugin that currently uses this setting is the Social plugin (example here).

provides: "social"
requires:
provides: "taxCodes"
- This will register getTaxCodesMethod
. We find the taxCodes provider in variantFormContainer.js. See an example via our Avalara plugin.

provides: "taxCodes"
requires:
provides: "taxSettings"
- This will register a template to appear in the Tax section of the dashboard. See our core Taxes plugin register.js for an example.

provides: "taxSettings"
requires:
label
name
icon
template
provides
provides: "ui-search"
- Registers a template to appear in the search UI. Currently, the search UI only uses a single template, so unless you remove the core search UI plugin, adding additional search templates won't really have an effect. For examples, check out the search UI register.js, which gets pulled into the navbar in navbarContainer.js.

provides: "ui-search"
requires:
provides: "userAccountDropdown"
- This will add a link to a logged-in user's dropdown menu. When you click on a user's image or name when logged in, you'll seeing a list of shortcuts they have permission to access. shortcut
s and userAccountDropdown
s are similar. The primary difference is that shortcut
s will check for permissions before displaying a link, while userAccountDropdown
s display the link to all logged-in users. userAccountDropdowns get passed into mainDropdown.js, then rendered to the DropDownMenu component in the same file. Currently, the only plugin that registers a userAccountDropdown
is the Accounts plugin register.js.

provides: "shortcut"
requires:
route
name
label
icon
template
provides
Route
route: "/dashboard/accounts"
The route
property registers the supplied route with Reaction. Once registered, you'll be able to visit this route by navigating directly to example.com/shop-prefix/your-route
. If you have more than one shop, you'll need to use the shop prefix when visiting a route directly. See also: Routing Docs, getShopPrefix, and Router.initPackageRoutes.
The route
property is named by the name
property, if provided.
Name
name: "accounts"
The name
property permits calling this route with Router.go
- e.g.
Router.go("accounts");
The name property also becomes a permission or role
that users can have. Having a role that corresponds to the name
within a registered route permits the user to visit that route (but only for the shopId they have that role for). For more details, check out our Routing Docs.
Template
template: "accountsDashboard"
The template
property can be assigned to a Blaze template, although where it is used depends on the value of provides
. If you'd like to use a React component in place of a Blaze template, you just need to wrap your component in a Blaze template. There's an example of this in the Email plugin template file.
Workflow
workflow: "coreProductWorkflow"
Workflows are currently used to reference a layout for a template. We'll dig into this a bit in the Layouts section, but a workflow is essentially a unique structure within a given layout. Some registry entries require a workflow to be specified, and this will determine the structure around the template. This applies for the layoutHeader
, layoutFooter
, notFound
template, etc.
Mostly, these are used when registering a route (with no provides
property). An example of this can be found in the product-variant register.js.
Layout
layout: "printLayout"
The layout
property can be used to force the app to render a specific layout. The primary use for this is to define routes with a custom workflow where perhaps a header, footer, etc. is not necessary. We leverage this in the orders register.js to define a PDF print layout for orders.
Description
description
This was primarily used in the Reaction Packages Grid. This field permits you to set a description for your registry entry. Although it's now deprecated, it's still useful for conveying information to other programmers. It may also potentially see use in the app again.
Icon
icon: "fa fa-globe"
Again, the icon
is a set of classes that are used to define an icon. The example above refers to a Font Awesome icon, but with some extra work, it could include other types of icons as well.
Label
label: "Orders"
The label
is the human readable name for a registry entry. This should be originally written in English. You may then use i18n files within each plugin to translate them into other languages.
Container
container: "core"
This is used to group plugins. Saved for later use.
The container
is used to define which part of the app a plugin belongs to. This permits us to put different registry entries in their appropriate sections, eg. permissions and shortcuts.
Priority
priority: 3
The priority
property takes a positive integer as its value. It determines the order that this registry entry is loaded. Lower values will load first.
Enabled
enabled: true
This determines if a registry entry is enabled or not.
Permissions
permissions: [{
label: "Create Product",
permission: "createProduct"
}]
The permissions
property allows you to define a new permission. The permissions label will be used for reference, and it will show up in the permissions list. If the registry entry is permission-controlled, it will also become the requirement for access. This property is used by the createProduct shortcut.
Audience
The audience
property is used to define what permissions are required to view a step in a workflow. Primarily this is used in our checkout workflow. This property will almost certainly not work the way you expect it to if it's used in a plugin. It's much more complex than simply defining which roles can access a route.
Meta
meta: {
actionView: {
dashboardSize: "md"
}
}
Currently, the only setting used within the meta
property is to set the dashboardSize
for the actionView
. The Email register.js is an example of this.
The Layout Property
The registry property takes an array of objects that are defined by the Layout Schema. Here's an example from our Core Accounts plugin:
layout: [{
workflow: "coreAccountsWorkflow",
layout: "coreLayout",
collection: "Accounts",
theme: "default",
enabled: true,
structure: {
template: "accountsDashboard",
layoutHeader: "layoutHeader",
layoutFooter: "",
notFound: "notFound",
dashboardHeader: "dashboardHeader",
dashboardControls: "",
dashboardHeaderControls: "",
adminControlsFooter: "adminControlsFooter"
}
}]
Workflow
workflow: "coreAccountsWorkflow"
The combination of workflow
+ layout
determines what gets rendered to the client. By looking up a workflow
+ layout
combination we can get the structure that's embedded here and render the correct templates to each section of the layout.
For examples of how the workflow and layout work together like this, dig into router.js
We also use workflows as a sort of state machine in the case of our checkout and order fulfillment processes. Checkout workflow.js to see how this works.
Layout
layout: "coreLayout"
For any given workflow/layout combination, the layout
property defines which macro-level layout should be used. Each layout can have a different structure. Our core layout component is defined in coreLayout.js and registered in index.js, along with our only other included layout, printLayout
.
Collection
collection: "Accounts"
The collection
property declares which Mongo collection is associated with this workflow/layout combination. It shouldn't be necessary to define this for most plugins.
Theme
theme: "default"
The theme
property defines which CSS theme to use. Currently, "default" is the only included option.
Enabled
enabled: true
The enabled
property turns this workflow/layout combo on or off.
Structure
structure: {
template: "accountsDashboard",
layoutHeader: "layoutHeader",
layoutFooter: "",
notFound: "notFound",
dashboardHeader: "dashboardHeader",
dashboardControls: "",
dashboardHeaderControls: "",
adminControlsFooter: "adminControlsFooter"
}
The structure
property is where specific templates can be assigned to a layouts areas. Each layout may have it's own unique structure and so some render locations may render in one layout and not in another.
Conclusion
I hope that this post has given you a better understanding of how a register.js
file for a Reaction plugin can be structured. There's a lot of information here, and while the initial read-through should do you some good, I'd recommend bookmarking this post for future reference.
If you have any implementation questions, or if you ever get stuck along the way, don't hesitate to join our Gitter chat. We have a general channel, as well as a Plugins channel. Our community is very active, and you'll find core Reaction developers to reach out to through these channels as well.
If this was helpful to you, or if you have any follow-up questions, let me know in the comments.
Thanks for reading!