Markus Oberlehner

Multi Export Vue.js Single File Components With Proxy Exports


In one of my previous articles, we examined how we can use JSX in Vue.js to export multiple Vue.js components from a single Single File Component (SFC) .vue file. Just recently I found an even easier solution to this problem.

Although it can be handy to use JSX for simple UI components, as I described in my article, there are some drawbacks to this approach. First, JSX doesn’t feel very natural in a Vue.js environment, and second, you lose many of the advantages of Vue.js Single File Components, such as scoped styles.

Proxy exports

The concept I’ve come up with is quite simple: All components live in their own SFC, but there is one master component that proxies all associated components.

Let’s say we have a custom form, and we want to split it up into multiple components for each part of the form.

components
├─ FancyTable.vue
├─ FancyTableBody.vue
├─ FancyTableCell.vue
├─ FancyTableHead.vue
└─ FancyTableRow.vue

If we want to use these generic form components to create a new, more specific component like a DataList component, it will look something like this.

<template>
  <FancyTable>
    <!-- ... -->
  </FancyTable>
</template>

<script>
// src/components/DataList.vue
import FancyTable from "./FancyTable.vue";
import FancyTableBody from "./FancyTableBody.vue";
import FancyTableCell from "./FancyTableCell.vue";
import FancyTableHead from "./FancyTableHead.vue";
import FancyTableRow from "./FancyTableRow.vue";

// ...
</script>

Let’s improve this somewhat by updating our FancyTable component to serve as a proxy component that also exports all associated components.

<template>
  <table class="FancyTable">
    <slot />
  </table>
</template>

<script>
// Export the root component as named export.
export const FancyTable = {
  name: "FancyTable",
  // ...
};

// Proxy export all related components.
export { default as FancyTableBody } from "./FancyTableBody.vue";
export { default as FancyTableCell } from "./FancyTableCell.vue";
export { default as FancyTableHead } from "./FancyTableHead.vue";
export { default as FancyTableRow } from "./FancyTableRow.vue";

// A Vue.js SFC must have a default export.
export default FancyTable;
</script>

Now our FancyTable component serves as a proxy for all related subcomponents. Shout-out to Philipp Kühn for showing me this shortened export / from syntax.


Do you want to learn more about advanced Vue.js techniques?

Register for the Newsletter of my upcoming book: Advanced Vue.js Application Architecture.


<template>
  <FancyTable>
    <!-- ... -->
  </FancyTable>
</template>

<script>
// src/components/DataList.vue
import {
  FancyTable,
  FancyTableBody,
  FancyTableCell,
  FancyTableHead,
  FancyTableRow,
} from "./FancyTable.vue";

// ...
</script>

As you can see above, we now only have to use one import statement to import all parts of the table we need to build our DataList component.

Wrapping it up

The benefits of this approach are most likely not a big deal, but in my opinion it can still be a useful improvement in certain situations.

Situations in which I already use this pattern are for generic table components such as in the example above and also for form components such as various types of input and general form layout components.

References