Site logo, of a flying rocket on a laptop.
Published on

Dynamic Components: A Vue.js feature that React developers shouldn't live without

Authors

As a full-stack developer who switched from Vue.js to React as my primary JavaScript framework, there were a few things I missed from Vue within React! The main one was dynamic components. This was driven by some legacy code I was working on that was incredibly painful to maintain.

Within Vue.js, you can use a dynamic component to actively switch between components on a page. This is particularly useful on platforms like content management systems (CMS), where a user will need to switch between a tabbed form or to generate UI programmatically from form builders.

So within Vue you have:

<!-- Component changes when currentTab changes -->
<component :is="currentTab" v-bind="{... other props}"></component>

Which you can pass either the Component as a property, or a reference to it. To learn more about Dynamic Components in Vue.js - (https://vuejs.org/guide/essentials/component-basics.html#dynamic-components).

Legacy code

So for a project that I started working on, we had some gnarly legacy code, which not only was pretty difficult to maintain generally, but was duplicated across a few pages within our Next.js application, which made the issue all the more apparent.

As part of our React render we had a very long (over 300+ lines…) switch statement to programmatically load components. With the majority of the data mapping between cases being the exact same properties being passed to the component. (There were 1-2 which handled properties slightly differently, but with a minor refactor we had a consistent api between the inbound data and the components themselves).

switch (contentTypeId) {
            case 'component1':
              return (
                <div>
                  {item && item?.fields && (
                    <Component1 componentProps={cms.data} />
                  )}
                </div>
              );
            case 'component2':
              return (
                <div>
                  {item && item?.fields && (
                    <Component2 componentProps={cms.data} />
                  )}
                </div>
              );
			// ... Several hundred more lines of cases. (Seriously!)
}

So we set off to replace the general functionality of this approach within our React code base, taking a more Vue.js like approach to hopefully simplify the code, and make it considerably more testable and maintainable for the rest of the team.

So after a slightly (a lot) less refined attempt, we came up with the below, which enabled us to dynamically load in components relative to the data we received from our headless CMS. This meant that we just had to implement this component rather than the HUGE switch statement, and we just needed 1 set of tests for that component.

const Components = {
  componentId: Component,
 // ... All other components to expose
};

const DynamicComponent: FC<ComponentProps> = ({
  componentId,
  data,
}) => {
  return typeof Components[componentId] !== 'undefined'
    ? React.createElement(Components[componentId], {
        block: componentId,
        ...data,
      })
    : null;
};

export default DynamicComponent;

Overall the Developer Experience (DX), has been considerably better (We definitely broke parts of the site a few times whilst working with the legacy code!) and more sustainable.