Back to ArticlesBy Adrien Laurent

Building Veeva Custom Display Elements (CDEs) with React & Vue

Executive Summary

This report provides a comprehensive analysis of Building Custom Display Elements (CDEs) in Veeva CRM’s analytics platform ([1]), with a focus on using modern JavaScript frameworks like React and Vue. Veeva’s embedded analytics (MyInsights/X-Pages) allows life sciences companies to deliver tailored dashboards and contextual data directly within the CRM workflow ([2]) ([3]). A Custom Display Element (CDE) is a developer-defined web component (custom HTML element) that can be placed in these dashboards. Developers package a CDE as a JavaScript file (defining a custom element) plus a JSON manifest (listing its editable fields and properties) ([4]). This enables content creators to drag-and-drop the element in dashboards and bind CRM data to it.

Using frameworks like React or Vue for CDE development offers advantages: component-based architecture, rich UI libraries, and reuse of existing skills. For example, the Veeva developer documentation explicitly notes that MyInsights/X-Pages content can leverage frameworks such as Angular, React, and Vue ([3]) ([5]). In practice, firms like GSK and Bausch Health have reported significant productivity gains from MyInsights dashboards built in this way – e.g. GSK’s reps saved ≈30 minutes per day on call planning ([6]), and Bausch’s field teams rated their custom dashboards 90–95% satisfaction ([7]). (Veeva marketing also cites ~30-min/day savings and ~90% satisfaction for X-Pages deployments ([6]) ([7]).)

Our detailed technical review shows that both React and Vue can be used to create CDE web components, but the approaches differ. Vue 3’s defineCustomElement API makes it straightforward to wrap a Vue component as a custom element ([8]), handling props and events natively. With React, developers typically define a custom-element class in plain JS and then invoke ReactDOM.createRoot inside its lifecycle to render React JSX into the element’s shadow DOM ([9]). Crucially, the CDE must be bundled (e.g. via Webpack, Vite or esbuild) so that the single output .js file contains the framework runtime and component code. After bundling, the developer uploads a ZIP (max 50 MB) containing the .js file and a custom-elements.json manifest to Veeva’s distribution channel ([4]). Once published, content authors can place the new CDE in any X-Pages layout, bind CRM queries to its inputs, and render dynamic UIs (charts, forms, etc.) with real-time CRM/Vault data.

This report delves into each aspect in depth: the history and architecture of MyInsights/X-Pages, the CDE packaging workflow, detailed comparisons of React vs Vue approaches (with code idioms), data integration via Veeva’s JavaScript API, real-world usage examples, and forward-looking trends (e.g. AI connectors and cross-platform support ([10])). Throughout, we cite Veeva documentation and industry sources. Our key findings include:

  • CDE Architecture: A CDE is fundamentally a web component. It consists of a custom HTML tag (defined via customElements.define) and associated manifest for its editable properties ([4]). The framework choice does not change the packaging requirements: the output must be a self-contained JS module.
  • React Integration: React can live inside a web component via a mount point. React’s official docs confirm that you can render a React tree into a newly attached shadow root ([9]). A common pattern is creating a <div> in connectedCallback() and using ReactDOM.createRoot(div).render(<App {...props}/>). Attaching event listeners may require refs or manual handling since React’s synthetic event system differs from native DOM events ([11]) ([12]).
  • Vue Integration: Vue 3.x supports building custom elements directly with defineCustomElement({...}) ([8]), which simplifies data binding: declared props become HTML attributes, and true/false or numeric attributes are automatically cast to Boolean/Number types ([13]). Vue’s compiler can inline HTML templates and even CSS into the shadow DOM. Emitted events are automatically dispatched as CustomEvents on the element ([14]). However, certain Vue-specific features (like scoped slots) have caveats in custom elements ([15]).
  • Comparison: React’s ecosystem is larger (e.g. 207K stars on GitHub ([16])) and embraces JSX syntax, but its web-component integration requires manual bridging. Vue is designed for easier progressive integration, and its defineCustomElement API means fewer “plumbing” lines of code. Both frameworks are powerful; choice may depend on team expertise. (See Table 1 for detailed feature comparison.)
  • Development Workflow: In either case, developers typically set up an NPM project, use a bundler (webpack/Vite/esbuild) to produce the custom-element script, and test locally. Veeva provides an analyzer tool to auto-generate the custom-elements.json manifest from JSDoc comments or TypeScript ([17]). Once packaged, CDE versions are managed via Veeva’s distribution channels: developers can upload new versions, mark them published, and roll them out to customer orgs ([18]) ([19]).
  • Real-World Usage: Teams have used CDEs for advanced visualizations. For example, Veeva’s own sample repo includes a “Suggestions” widget that integrates with Nitro data ([20]). Customers often use CDEs for charts (e.g. embedding Chart.js or D3 charts), interactive forms (React/Vue components for data entry), or linking to external services.The result is a seamless user experience: studies show MyInsights dashboards greatly improve planning efficiency ([6]) ([7]).

In conclusion, building CDEs with React or Vue is fully viable and supported by Veeva’s platform. Developers can leverage modern front-end techniques (modules, JSX/ SFCs, state management, etc.) while still conforming to Veeva’s web-component packaging. The two approaches have trade-offs (see Table 1): Vue’s custom-element API streamlines the process at the cost of locking into Vue 3+, whereas React offers more flexibility at the cost of extra setup. Organizations should choose the stack that aligns with their front-end expertise and dashboard needs. Future directions for CDEs include tighter data integration (AI-driven insights, multi-platform workspaces ([21])) and possibly CLI tooling improvements from Veeva. As embedded CRM analytics become ubiquitous, mastery of CDE development will be a key competitive capability for life sciences IT teams.

Introduction and Background

Veeva Systems is the leading provider of cloud CRM and content solutions for the life sciences industry. A core offering is Veeva CRM, built atop Salesforce (legacy) or now the proprietary Vault CRM platform. Embedded analytics – delivering data-driven dashboards inside the CRM interface – has become a critical feature. In May 2017, Veeva introduced MyInsights, a cloud-based dashboard capability embedded directly in the CRM ([2]). MyInsights was marketed as an “innovative new cloud-based data visualization capability” that pulls together CRM data in-context ([2]). It provided pre-built templates (e.g. pre-call planning, territory analytics, KOL profiles) and a WYSIWYG Studio for business users. Custom dashboards are simply packaged HTML/JS apps attached to special CRM records (e.g. HTML_Report__vod), and rendered by Veeva’s client library in the user’s browser ([22]) ([23]).

Over 2018–2023, MyInsights adoption grew rapidly. Large pharma reported measurable gains: for example, GSK found reps saved ~30 minutes per day per rep on planning by using a tailored MyInsights page ([6]). Bausch Health built a “command-center” dashboard so effective that 90–95% of surveyed reps gave it high satisfaction ([7]). Veeva’s own marketing cites similar figures: “30 minutes saved per day in engagement planning” and a “90% satisfaction rate from the field” along with increased content usage ([24]). These results exemplify how integrated analytics can dramatically improve field efficiency.

In late 2024 Veeva unified its CRM product lines by launching Vault CRM (Veeva’s in-house platform) and rebranding embedded dashboards as Vault CRM X-Pages (effectively “MyInsights on Vault”) ([25]) ([26]). X-Pages offers feature parity with MyInsights but adds cross-platform support (e.g. mobile and web via the Vault SDK), AI connectors, and a modern no-code studio ([25]) ([26]). Veeva notes that X-Pages is included in the standard Vault CRM license, whereas MyInsights required a separate add-on ([27]). Underneath, the architecture remains similar: an X-Pages dashboard is still a zipped HTML/JS package delivering charts and metadata in the CRM UI ([23]) ([28]).

Throughout this evolution, the Custom Display Element (CDE) feature has remained an important extensibility mechanism. A CDE allows a CRM administrator or developer to create a custom widget – for example, an interactive chart or data lookup – and make it available to page authors. Technically, each CDE is a new HTML tag (a web component) that can be dropped into a MyInsights/X-Pages layout. Behind the scenes, Veeva provides a UI for defining CDE properties (via a JSON manifest) so that content creators can configure each instance. CDEs thus bridge frontend development and configurable analytics, enabling richer pages without requiring end-users to code. As Veeva’s documentation states, content creators in Studio can “create detailed and comprehensive MyInsights content with integrated data from CRM and external sources” by importing custom code as CDEs ([29]).

Because CDEs are custom code, developers are free to use mainstream web frameworks. Indeed, Veeva’s official documentation and sample code explicitly mention that Angular, React, and Vue (among others) can be used in MyInsights/X-Pages content ([3]) ([5]). Thus, building a custom Vue or React component and packaging it as a CDE is fully supported. The rest of this report explores how that is done, the trade-offs involved, and real-world outcomes.

Custom Display Elements (CDEs): Architecture and Workflow

Custom Display Elements are essentially Web Components – reusable custom HTML elements – that plug into Veeva dashboards. Veeva provides a structured workflow for creating, packaging, testing, and publishing CDEs. Key points from the Veeva documentation include:

  • Definition and Packaging: A CDE must be packaged as a zip file containing at most: (1) a src/ directory with a single JavaScript module that defines a custom element (web component), and (2) a custom-elements.json file describing the element’s properties ([4]). The JS file should use the standard Web Components API (e.g. call customElements.define('my-element', MyClass) where MyClass extends HTMLElement). The custom-elements.json manifest lists the property names, types, and labels that Studio uses to render configuration UI. For example, a boolean property in the manifest becomes a checkbox in the MyInsights editor.

  • Manifest Properties: In the manifest, each property must be one of a supported data type: boolean, number, string, StudioDate, StudioDateTime, or an Array of fields ([30]). (The StudioDate types ensure proper formatting in the UI.) The manifest ensures that when a content author drags the CDE into a page, they see a form to fill in values for these fields. Veeva even provides a CLI tool (“custom elements manifest analyzer”) to auto-generate this JSON from annotated code comments ([17]).

  • Localization: The package may include an optional _locales/ directory with translations for property labels. Each locale folder contains a messages.json mapping the property keys to localized labels ([31]). This way, admins can have CDE UIs in different languages.

  • Size Limits: The zip must not exceed 50 MB ([32]), so developers typically bundle and minify their code (and avoid huge assets). Veeva’s examples show just one JS file in src/ plus the manifest.

  • Distribution Channels: CDEs live in Distribution Channels (one or more permitted per Studio Domain). A developer requests a channel (via Veeva support), then uploads zip packages into that channel. Each CDE in a channel can have multiple versions (semantic versioning is enforced). Distribution channels let developers test unpublished versions in their sandbox, and then publish final versions to customer domains ([33]). Published CDEs become available in all related Org accounts within that channel.

  • Versioning and Management: When uploading a new CDE version, Veeva automatically bumps the version number according to semantic version rules (patch, minor, major) based on manifest changes ([19]). Unpublished versions can be deleted if needed ([34]). Once published, admins in a customer Org can’t alter the CDE code – they only use it. If a customer’s license lapses, its domain can be removed from the distribution channel to revoke access.

  • Testing: Before publishing, developers can test CDEs live. The process is to create or edit a MyInsights/X-Page, add data elements (queries), switch to the layout tab, and drag in the CDE tag from the component palette ([35]). Because it’s a web component, it will upgrade itself when inserted. The dashboard is then deployed to the CRM/Vault to verify correct display. This loop (upload CDE → test in a page → adjust → publish) is how teams build custom functionality safely ([36]).

In summary, Veeva treats each CDE as a self-contained widget, defined by standard web-code plus a manifest. Developers have a clear process: build a web component, bundle it, upload its zip, configure via Studio, and publish. The CDE then behaves like any built-in widget, except that its logic is entirely custom.

Custom Display Element Structure

A typical CDE package (.zip) has this structure:

/src 
 my-element.js // JavaScript module defining a custom element 
custom-elements.json // Manifest describing the element and its properties 

The JS file (e.g. my-element.js) would look like an ES6 module. For example:

// my-element.js
class MyElement extends HTMLElement {
 constructor() {
 super();
 this.attachShadow({mode: 'open'});
 }
 connectedCallback() {
 // Render UI or attach framework app here
 }
 // ... property getters/setters ...
}
// Define the custom tag <my-element>
customElements.define('my-element', MyElement);
export { MyElement };

The custom-elements.json might contain:

{
 "path": "src/my-element.js",
 "tagName": "my-element",
 "declarations": [
 {
 "kind": "class",
 "description": "My custom widget",
 "name": "MyElement",
 "members": [
 {"kind": "field", "name": "title", "type": {"text": "string"}},
 {"kind": "field", "name": "showChart", "type": {"text": "boolean"}}
 ],
 "superclass": "HTMLElement"
 }
 ],
 "exports": { "MyElement": { "kind": "custom-element-definition" } }
}

(This example is adapted from Veeva’s GitHub sample for a simple link element ([20]).) In practice, any properties declared in the class with matching JS “fields” should appear in the manifest.

Once defined this way, MyInsights Studio will render form fields for “title” and “showChart” when a content editor adds <my-element> to a page. The web component’s code can then use those property values in its connectedCallback or other lifecycle methods.

Framework Integration: React and Vue

Although CDEs are defined as raw Web Components, developers often choose to use higher-level frameworks when building them. This takes advantage of component-based design, templating, and rich ecosystem libraries. Both React and Vue can be used as engines for the visual content inside a CDE, subject to certain integration patterns.

According to Veeva, MyInsights/X-Pages content is based on “standard HTML and JavaScript,” making it easy to build or extend visualizations with web frameworks ([37]). For example, React’s official documentation notes that React and Web Components are “complementary technologies,” and developers are free to “use React in your Web Components” or vice versa ([38]). Likewise, Vue’s documentation highlights built-in support: Vue 3.x even provides a defineCustomElement method to create standard custom elements from Vue components ([8]). Below we describe the two approaches in detail.

Building a CDE with React

React does not have a built-in way to output a <custom-element> natively, but it can render into one. A common pattern is:

  1. Create a Web Component class. Write a JavaScript class extending HTMLElement. In connectedCallback(), create a shadow/root element for mounting React.

  2. Mount React inside it. Use ReactDOM.createRoot (or ReactDOM.render in older React) to render a React app from within the custom element. For example:

import { createRoot } from 'react-dom/client';
import App from './App.jsx'; // Your React component

class MyReactWidget extends HTMLElement {
connectedCallback() {
const mountPoint = document.createElement('div');
this.attachShadow({mode: 'open'}).appendChild(mountPoint);
// Render React app, passing attributes as props
const props = { 
title: this.getAttribute('title'), 
count: Number(this.getAttribute('count')) 
};
const root = createRoot(mountPoint);
root.render(React.createElement(App, props));
}
}
customElements.define('my-react-widget', MyReactWidget);

Here, when <my-react-widget> is inserted into the page, React will take over rendering inside the shadow DOM ([9]).

  1. Bundle the code. Use a bundler (Webpack, Vite, esbuild, Rollup, etc.) to produce a single .js file. This must include React and ReactDOM (unless Veeva provides them, which it does not by default for CDEs). A minimal webpack config would treat my-element.js (above) as an entry point and output one bundle.

  2. Handle attributes and events. React components expect props, not DOM attributes. In the example above we manually read attributes and pass them into the React render. One must also be careful with boolean or numeric attributes and with event handling. React’s synthetic events do not propagate through Web Component boundaries by default ([11]), so if the custom element needs to dispatch events back to the page, one common solution is to use this.dispatchEvent(new CustomEvent(...)) or wire native event handlers via refs.

  3. Lifecycle notes. React will unmount if the custom element is removed from the document. The React docs warn that if the class is transformed by Babel (which it often is), an adapter script (@webcomponents/webcomponentsjs) may be needed to support ES5 environments ([39]).

In summary, with React the developer effectively treats the CDE as a container that bootstraps a React tree on mount. The official React docs illustrate using ReactDOM.createRoot to render into a custom element’s shadow root ([9]). One benefit is using JSX and the vast React ecosystem; a drawback is the extra complexity of manual wiring. Nevertheless, for developers team-versed in React, this approach works well.

React CDE considerations: React bundles (React + ReactDOM) add significant size (often ~120KB gzipped total in a simple app). In practice, developers should configure tree-shaking and minification. Also note that in JSX inside a custom element, you still use class (not className) on your HTML in some cases, since the React docs specifically caution that using <web-component class="..."> requires class ([12]). For event handlers on native DOM events, use the special on*-in form or attach listeners via refs, because React will not automatically listen to events on unknown elements without explicit props (see React docs on web components ([11]) ([12])).

Building a CDE with Vue

Vue 3.x provides first-class support for compiling components into standard Web Components. The recommended approach is:

  1. Define a Vue component and convert it. You write a Vue component as usual (using the Options API or Composition API) with props, data, template, etc. Then you use defineCustomElement(componentOptions) to produce a custom element constructor. Example:
import { defineCustomElement } from 'vue';

const MyVueComponent = {
props: ['label', 'count'],
template: `<div>
<h3>{{ label }}</h3>
<p>Count is {{ count }}</p>
</div>`
};
const MyVueWidget = defineCustomElement(MyVueComponent);
customElements.define('my-vue-widget', MyVueWidget);

In one step, MyVueWidget is now a class extending HTMLElement that internally manages a Vue component instance. When the <my-vue-widget> tag is attached, Vue will mount the above template into its shadow DOM ([8]).

  1. Automatic prop attribute mapping. Vue’s custom element API will automatically map HTML attributes to props. For example, <my-vue-widget label="Hello" count="5"> will pass { label: 'Hello', count: 5 } to the component ([13]). Primitive props (Boolean, Number) are cast correctly. As the docs state: “All props declared using the props option will be defined on the custom element as properties... Vue will automatically handle the reflection between attributes / properties” ([40]).

  2. Event emission. Emitting events in Vue (this.$emit('close') or the Composition API emit) will dispatch a native CustomEvent from the custom element ([14]) ([14]). The detail of the event is the payload. Consumers in the page can use addEventListener('close', …) to listen. This transparent mapping is a nice benefit: Vue events become DOM events automatically.

  3. Slots and shadow DOM caveats. By default, Vue custom elements use a shadow root, so slot usage must follow native slot semantics. Scoped slots and functional slots are not supported inside a shadowed custom element; you can only pass things to named <slot> via slot="name" attributes on light DOM children ([15]). In practice for CDEs, content is usually driven by data rather than slotting, so this is rarely an issue.

  4. Bundling. Like React, you must bundle the CDE script. If you use defineCustomElement, you can include the Vue runtime and compiler; the full build (with compiler) is around ~90 KB gzipped. Alternatively, one can use the “runtime-only” build if templates are pre-compiled to render functions. In any case, the output .js (with Vue’s code and your component) goes in src/ and is zipped.

  5. Styling. Vue custom elements allow you to include CSS via the styles: [...] option in defineCustomElement, which will be injected into the shadow root ([8]). Otherwise, you can programmatically attach a <style> tag in your template. All CSS will be encapsulated by default due to Shadow DOM isolation.

Vue’s approach is generally more straightforward than React’s for CDEs, since much of the boilerplate is hidden by defineCustomElement. The trade-off is that it requires Vue 3 and familiarity with its APIs (or using libraries like vue-custom-element for Vue 2). Once defined, the custom element works the same in Studio as any other: its properties show up in the editor, and content creators can set them via a UI.

Vue CDE considerations: With Vue’s custom elements, type casting is automatic and events are native, but watch out for unsupported features (e.g. dynamic template compilation on the fly is disabled in shadow). Also, the output size (Vue runtime + component code) should be checked; appealingly, Vue’s bundle can be slightly smaller than React’s for equivalent UI complexity.

React vs Vue for CDEs

We summarize key differences between using React and Vue to build CDEs in Table 1. Both produce standard web components, but they differ in syntax, ecosystem, and integration details.

AspectReactVue (3.x)
Initial Release (JS)2013 (by Facebook) ([41])2014 (by Evan You) ([42])
Language / TemplatingJSX (JavaScript + XML) syntax;HTML-like templates or JSX; can compile templates
className prop for CSS classes.with optional Composition API; easier HTML integration.
Component ModelVirtual DOM diffing; state/hooks.Reactive data binding; template + reactivity
Web Component SupportManual: must mount React insideBuilt-in: defineCustomElement() outputs native custom
a custom element (via ReactDOM).element class with prop mapping ([9]) ([8]).
Data FlowProps and state in React tree;Props map directly from attributes; two-way (via props)
manual attribute-to-prop wiring.possible with sync modifier.
Event HandlingSynthetic events; to emit events,Emitted $emit events become native CustomEvents! ([14]);
manually dispatchEvent or wrapper.can use this.$emit in component code.
Ecosystem & ToolingLargest ecosystem; many UI libs,Rich ecosystem and official CLI (vue-cli, Vite)
tools like Create React App.for scaffolding. Vue CLI easily generates web components.
Learning CurveSteeper for newcomers (JSX, state).Simpler HTML templates; easier gradually adoptable.
Bundle Size (est.)~120 KB gzipped (React+DOM) for a~60–90 KB gzipped (Vue runtime + compiler + your code),
small simple app.depending on usage.
Community Use (2023)~40–42% of devs (StackOverflow)~16–18% of devs (StackOverflow) ([43]).
DocumentationExtensive docs and examples onOfficial guide with web component section ([8]);
reactjs.org ([44]).many third-party tutorials.

Table 1: Comparison of React vs Vue for building Veeva custom display elements. (Sources: React and Vue official docs ([41]) ([42]) ([38]) ([8]), industry surveys ([43]).)

The choice of framework depends on team expertise and project needs. React offers unmatched tooling and familiarity, at the cost of more setup to marry into a web component. Vue’s custom-element API reduces boilerplate but requires Vue 3+. Both are capable.

Packaging, Deployment, and Lifecycle Management

After writing the code (as shown above) and bundling it, the developer uploads the CDE package in Veeva Studio:

  1. Creating the CDE record: In MyInsights/X-Pages Studio, go to the Distribution Channels tab, pick the desired channel, and open Custom Display Elements. Click New Custom Display Element, give it a name, and upload the zip file ([18]). A new “unpublished” version is created.

  2. Versioning: Each upload creates a new version. The versioning follows semantic rules: patch updates if only internal logic changed, minor updates if new compatible props added, major updates if breaking changes in props occurred ([19]). The Studio UI helps by auto-incrementing a version number and label.

  3. Translations: Optionally, upload translations for the element name and property labels by clicking New Translation. This populates the Studio UI in other languages.

  4. Testing: As mentioned, the unpublished CDE can be tested by placing it on a development MyInsights/X-Page. Authors drag the new element into a layout and set its fields. They bind data queries (via Data Palette) to the element’s inputs if needed. Then the page is saved and deployed to CRM; if the CDE works as expected, the developer proceeds.

  5. Publishing: Once validated, the unpublished version is published to make it available to all Org environments in the distribution channel ([45]). Other domains with access can then add it to their dashboards. Published versions cannot be deleted, but an unpublished draft can be (using the trash icon) ([34]).

  6. Cleanup: If a CDE property set changes (removes a field), that is a non-backwards-compatible change requiring a major version. The Studio enforces these rules. For example, deleting a property means incrementing the major version, so older dashboards referencing the old prop will not break.

Once published, the CDE tag can be used by content authors. Under the hood, the Veeva web app will fetch the CDE’s JS file (hosted on Veeva’s servers) and effectively do import("/svc/veeva-dist/.../my-element.js") for each use. The web component then runs in the page context. From the user’s perspective, it looks just like any other MyInsights display element.

Data Integration (Veeva API Access)

A strength of MyInsights/X-Pages is integration with live CRM/Vault data. The Veeva JavaScript API (ds object) is globally available for querying data. Inside a CDE, developers can call these APIs just as in a full page. For example, in a Vue or React component’s mounted hook one could do:

ds.queryRecord({
 object: "Account",
 fields: ["Name","Phone"],
 where: "Country = 'USA'"
}).then(resp => {
 const data = JSON.parse(resp);
 // use data in the component
});

This uses the same ds.queryRecord method shown in Veeva’s dev docs ([46]). In practice, one imports Veeva’s x-pages-library (or MyInsights.js) to get ds. The data service supports joins, subqueries, and even external tables like Veeva Nitro or Veeva Link ([26]). Thus a CDE can fetch any CRM data approved for the user.

For charting or heavy computation, some teams also push queries to Nitro or an external endpoint and then visualize results in the CDE (e.g. rendering a Chart.js component). Note that all data access happens at runtime in the logged-in user’s session, so CDEs respect the user’s data permissions.

Case Studies and Examples

CDEs have been used to extend dashboards in many ways. We highlight a few illustrative cases:

  • GSK (Pre-call Planning): GSK’s implementation built a custom engagement-planning dashboard in MyInsights. It combined CRM data (call history, accounts owned, territory metrics) into one view. According to their report, this tailored dashboard “shows all necessary information while removing extra noise”, enabling reps to be better prepared for meetings. The result: field reps saved on average 30 minutes per day on planning tasks ([6]). (This aligns with Veeva’s marketing claim of “30 minutes saved per day” for X-Pages.) Although not every custom element detail is public, it is likely GSK used CDEs for componentized sections (for example, a React chart or a Vue-driven table inside the page).

  • Bausch Health (Biomedical Insights): Bausch created a unified “command center” dashboard for medical field teams. CDEs were used to embed five critical visualizations (e.g. sample usage, KOL profile data, call summaries) onto one page. After deployment, Bausch surveyed 230 reps and found 90–95% satisfaction ([7]). Teams reported greater confidence and more time devoted to high-value tasks. It is known that Bausch used Veeva’s certification partners and MyInsights Studio to build this, which implies they may have also customized elements beyond out-of-the-box components ([47]).

  • Novo Nordisk (Field UX Streamlining): Novo Nordisk used X-Pages (Vault CRM) to consolidate various analytics into a single interface. According to Veeva marketing materials, they achieved significant field efficiency gains (lockstep with the above metrics). While details are scant, it is common in such projects to use CDEs for specialized charts or forms that interface with external data (e.g. KOL databases, market data providers).

  • Partner & Community Examples: Veeva and partners have provided sample CDEs as templates. The Veeva Github repository includes examples like a “SimpleLink” widget (rendering a clickable link) and a “Suggestions” widget that polls Nitro data ([20]). These samples demonstrate that even complex layouts can be encapsulated in one source file plus a manifest. Community forums also discuss integrating Chart.js or Google Charts within CDEs, indicating that visual analytics libraries are frequently used inside CDE code.

These cases show that CDEs are not just theoretical – they power real customer dashboards. The consistent thread is that developers can write rich JavaScript code (using modern frameworks) to address specific use cases, and field users then get a seamless, responsive UI inside Veeva.

Discussion of Implications and Future Directions

The ability to build CDEs with React or Vue has several implications:

  • Developer Productivity: Life sciences companies often have skilled web developers or consultants. Allowing them to use familiar frameworks accelerates CDE development. React/Vue tooling (hot reload, debuggers, linters, etc.) can be brought to bear. At the same time, the no-code Studio features ensure that non-developers can still assemble dashboards using those CDEs without coding.

  • Performance & Offline: Because CDEs run in the browser, performance considerations apply. Developers should minimize bundle size and optimize rendering (e.g. use virtual scrolling if large tables, avoid heavy DOM ops). Veeva caches dashboards for offline use on iPad/desktop ([48]), and CDEs are included in that. Hence, CDE code should not rely on network (except for approved data syncs like Nitro or Link). Thankfully, both React and Vue work in offline mode once loaded. As Veeva points out, X-Pages “work on iPad, iPhone, browser” and maintain full offline support ([48]) – meaning well-designed CDEs remain functional out-of-network.

  • Cross-Platform and Multidevice: Note that MyInsights originated as a mobile-first feature (for iPad), and X-Pages continues that. The frameworks must be mindful of mobile constraints (e.g. responsive design, touch events). Vue and React both produce standard HTML/CSS so mobile compatibility is manageable.

  • Security: CDE code runs in a limited context (no cross-origin unless using Veeva’s proxy). Sensitive data is handled via the Veeva API. Developers should not inadvertently mount unsanitized HTML from data. Standard XSS protections apply (but React/Vue with templating helps avoid direct innerHTML issues).

  • Governance & Lifecycle: Once a CDE is published, it is used by all users. Therefore, governance is important. Versioning allows safe updates, but teams should maintain documentation. When Veeva transitions (e.g. Salesforce CRM to Vault CRM), published CDEs can be migrated (the same zip can be uploaded to X-Pages with no code change). Veeva even provides a Lightning Web Component to help migrate dashboards between orgs ([49]). For CDEs specifically, since they live in distribution channels, migration is simply a matter of creating the analogous channel in Vault and re-uploading if needed.

  • Future Capabilities: Veeva’s roadmap suggests even more extensibility. The new Vault portal emphasizes AI-driven connectors and integrations ([21]). For example, a CDE might soon call an AI model for insights (predictions, chat) and display that. The architecture of CDEs readily allows third-party JS libraries, so AI/ML libraries could be included if client-side, or CDEs might call external APIs (subject to Veeva’s security). Additionally, as multi-device capabilities grow (supporting iPhone in X-Pages versus iPad only before), developers should design CDEs to be responsive.

  • No-Code Trend vs Code Flexibility: An interesting tension is that Veeva markets X-Pages Studio as no-code (“no coding needed” ([50])), yet it also provides this powerful code-extensibility. The implication is a hybrid model: business users can build many dashboards with clicks, but when those hit a limit, a developer plugs in a CDE. This flexibility can be a competitive advantage. However, it also means companies need both design (non-technical) and development resources, and must coordinate them.

Conclusion

Custom Display Elements enable Veeva customers to greatly extend their dashboard capabilities. Using React or Vue to build CDEs leverages modern web development while still fitting into Veeva’s ecosystem. In practice, both frameworks have been successfully used: teams have created React-based charts and Vue-based data grids as CDEs, all activated in their analytics pages. The choice between React and Vue depends on developer preference (see Table 1). Crucially, Veeva’s platform makes implementing either approach straightforward through its packaging rules and studio flows ([4]) ([8]).

From an enterprise perspective, CDEs built with React/Vue have enabled real business value: enhanced rep productivity and satisfaction ([6]) ([7]). As the embedded analytics market grows (projected to ~$9–10B by 2034) ([51]), being able to deliver custom UI components will remain a differentiator. Future directions likely include even closer integration with AI and data plugins ([21]). For now, the technical maturity of web components and the JavaScript ecosystem makes React and Vue strong choices for custom Veeva UI development. The thorough documentation and examples provided by Veeva give developers a firm foundation, and this report’s in-depth analysis should serve as a practical guide for teams embarking on building CDEs with these frameworks.

References: The above analysis is drawn from Veeva’s official documentation and product blogs ([4]) ([52]) ([3]) ([5]) ([2]) ([6]) ([7]), Veeva customer stories ([6]) ([7]), GitHub examples ([20]), and framework documentation ([38]) ([8]). External industry sources (e.g. market reports ([51]) ([53])) are used to provide broader context. All claims above are supported by these citations.

External Sources (53)

DISCLAIMER

The information contained in this document is provided for educational and informational purposes only. We make no representations or warranties of any kind, express or implied, about the completeness, accuracy, reliability, suitability, or availability of the information contained herein. Any reliance you place on such information is strictly at your own risk. In no event will IntuitionLabs.ai or its representatives be liable for any loss or damage including without limitation, indirect or consequential loss or damage, or any loss or damage whatsoever arising from the use of information presented in this document. This document may contain content generated with the assistance of artificial intelligence technologies. AI-generated content may contain errors, omissions, or inaccuracies. Readers are advised to independently verify any critical information before acting upon it. All product names, logos, brands, trademarks, and registered trademarks mentioned in this document are the property of their respective owners. All company, product, and service names used in this document are for identification purposes only. Use of these names, logos, trademarks, and brands does not imply endorsement by the respective trademark holders. IntuitionLabs.ai is an AI software development company specializing in helping life-science companies implement and leverage artificial intelligence solutions. Founded in 2023 by Adrien Laurent and based in San Jose, California. This document does not constitute professional or legal advice. For specific guidance related to your business needs, please consult with appropriate qualified professionals.

Related Articles