Building a custom Druxt module
Extend DruxtModule to build your own Druxt-powered, themeable component backed by Drupal data.
Every Druxt component (entities, views, menus) is built on the same base
class: DruxtModule. In this tutorial you'll extend it yourself to build a
DruxtCustomModule that:
- registers as a first-class Druxt module,
- renders through a theme component resolved by Druxt's suggestion system, and
- fetches its own data from Drupal through the DruxtStore.
You'll know each step worked because each one has a visible outcome.
Prerequisites
- You completed Getting started with Druxt.js
and the quickstart is running locally (
npm run dev). - At least one Article is published in the backend.
- You're comfortable reading a Vue single-file component.
Step 1: Scaffold the module and its theme component
Create nuxt/components/druxt/CustomModule.vue:
<script>
import DruxtModule from 'druxt/dist/components/DruxtModule.vue';
export default {
name: 'DruxtCustomModule',
extends: DruxtModule,
druxt: {
componentOptions: () => [['Default']],
propsData: ({ model }) => ({ value: model }),
slots(h) {
return {
default: (attrs) => h('div', { attrs }, [this.model]),
};
},
},
};
</script>
What the druxt() options mean:
componentOptionsfeeds the component suggestion system with names to look for:[['Default']]means Druxt will look for a theme component atcomponents/druxt/custom-module/Default.vuebefore falling back to its own defaults.propsDatamaps data from the module to props passed to whatever theme component is resolved.slotsdefines the default slot's render function: the actual content your module provides.
Then create the theme component it will resolve to,
nuxt/components/druxt/custom-module/Default.vue:
<template>
<strong>
<slot />
</strong>
</template>
<script>
export default {
props: {
value: {
type: String,
default: '',
},
},
data: ({ value }) => ({ model: value }),
};
</script>
Finally, use the module on a page. Create nuxt/pages/custom.vue:
<template>
<Druxt module="custom-module" value="Hello Custom Module" />
</template>
Outcome: open http://localhost:3000/custom. You see Hello Custom
Module in bold. The <Druxt> component resolved module="custom-module"
to your DruxtCustomModule (registered automatically, because the druxt
Nuxt module enables component auto-discovery), which rendered through your
Default.vue theme component. No manual component registration anywhere.
Step 2: Change the theme without touching the module
Druxt components delegate their wrapper to the theme layer, and you can
re-wrap them at the point of use. Edit nuxt/pages/custom.vue:
<template>
<Druxt module="custom-module" value="Hello Custom Module" :wrapper="{ component: 'marquee' }" />
</template>
Outcome: reload http://localhost:3000/custom. The same bold text now scrolls across the page. You just changed the rendered markup of a Druxt component from the outside, without editing the component itself. This wrapper-resolution mechanism is how whole-site theming works in Druxt; the theming guide covers it in depth.
Step 3: Fetch data from Drupal
So far the module rendered a hard-coded string. Real Druxt modules source
their data from the backend. The DruxtStore (the Vuex store that all
Druxt modules share) does the fetching and caching for you.
Update nuxt/components/druxt/CustomModule.vue:
<script>
import DruxtModule from 'druxt/dist/components/DruxtModule.vue';
export default {
name: 'DruxtCustomModule',
extends: DruxtModule,
data: () => ({
articles: [],
}),
async fetch() {
const collection = await this.$store.dispatch('druxt/getCollection', {
type: 'node--article',
});
this.articles = collection.data;
},
druxt: {
componentOptions: () => [['Default']],
propsData: ({ articles }) => ({ articles }),
slots(h) {
return {
default: (attrs) =>
h(
'ul',
attrs,
this.articles.map((article) => h('li', [article.attributes.title])),
),
};
},
},
};
</script>
And edit nuxt/pages/custom.vue back to the bare module:
<template>
<Druxt module="custom-module" />
</template>
Outcome: reload http://localhost:3000/custom. Instead of the greeting you now see a list of article titles, starting with the one you published in the Getting started tutorial. Publish another article in Drupal and reload: it appears too.
What just happened: the module's fetch() hook dispatched getCollection to
the DruxtStore, which asked your Drupal backend for all node--article
resources, cached the result, and handed the data back. The slot render
function turned each resource's attributes.title into list items, and the
resolved theme component wrapped them.
How it works
<Druxt module="custom-module">resolves to the component namedDruxtCustomModule: Druxt modules are just auto-discovered Vue components with a naming convention.- The
druxt()options drive the machinery: suggestions tell Druxt which theme components to look for,propsDatafeeds them,slotsprovides content. - Wrapper components and
props/slots/$attrspass-through mean the rendering is yours to override, whether per use, per site, or globally.
The same three ideas power every Druxt module in the modules list. When you're ready:
- Go deeper on overriding: Theme Druxt components.
- Understand why the suggestion chain looks the way it does: Component resolution.
- Read the base class reference: DruxtModule API.