Alrighty, let’s see if we can vibe code an extension for a Tableau Next dashboard now then.
I’ve given claude a list of documentation sources and access to my blogs. I have requested an extension plan and have let it work away reading and reasoning.
On it’s first go, it came up with this plan:
The goal is to build a Salesforce Lightning Web Component (LWC) that renders a D3 force-directed network chart and can be dropped into a Tableau Next dashboard as a custom extension. The project lives at /Users/chris/Git-Repos/HelloWorldLightningWebComponent, which is already a working SFDX project (API v66.0) containing only the basic helloWorld component from the blog tutorial.
The immediate deliverable is a chart on dummy data. A later phase will wire it to a specific Data Cloud / Data 360 semantic model via the Semantic Query API.
resourceLoader workaroundTableau Next runs on Lightning Web Runtime (LWR), which has restrictions that break the standard lightning/platformResourceLoader. The lwc-recipes-main project ships a resourceLoader utility component that works around this. We need to either copy that component into our project or replicate its logic. Without this the D3 script will silently fail to load.
analytics__Dashboard target in js-meta.xmlThe component will not appear in the Tableau Next extension picker unless the metadata file declares:
<target>analytics__Dashboard</target>
<isExposed>true</isExposed>
Standard targets (lightning__AppPage, etc.) are insufficient.
D3 cannot be loaded from a CDN inside a Salesforce org. It must be uploaded as a static resource (zip or individual file). The import pattern is:
import D3 from '@salesforce/resourceUrl/d3';
// then: await loadScript(this, D3 + '/d3.v5.min.js');
The recipes project already bundles D3 v5 at /Extension Testing/lwc-recipes-main/force-app/main/default/staticresources/d3/. We copy this into our project.
The libsD3 recipe (our blueprint) uses D3 v5. v5 uses d3.event for drag events, while v7 changed to passing the event as a parameter. Using v5 keeps our code directly comparable to the documented recipe. We can upgrade later.
The SDK is automatically injected by the Tableau Next runtime; there is no npm package to import. For now we focus on rendering the chart; SDK event wiring (filter/parameter subscriptions) is out of scope for Phase 1. The SDK entry point when needed is:
// accessed via DashboardWidgetComponentProps, passed into the component
const sdk = this.sdk; // injected prop
await sdk.getDashboardContext();
sdk.subscribe(SDK_EVENTS.FILTER_CHANGE, handler);
Fixed pixel dimensions (like the recipe’s 400×400) look poor in a dashboard tile that can be resized. We use a viewBox with preserveAspectRatio and width="100%" height="100%" so the simulation fills the available space.
lwc:dom="manual" requiredD3 manipulates the DOM directly. The SVG element must carry lwc:dom="manual" to opt out of LWC’s synthetic shadow DOM restrictions on direct DOM mutation.
d3NetworkChartforce-app/main/default/lwc/d3NetworkChart/
├── d3NetworkChart.html ← SVG container with lwc:dom="manual"
├── d3NetworkChart.js ← D3 force simulation logic
├── d3NetworkChart.js-meta.xml ← analytics__Dashboard target config
├── d3NetworkChart.css ← link/node styling
└── data.js ← dummy network data (business-themed)
resourceLoaderforce-app/main/default/lwc/resourceLoader/
└── resourceLoader.js ← copied from lwc-recipes-main (LWR workaround)
Source: /Users/chris/Git-Repos/Extension Testing/lwc-recipes-main/force-app/main/default/lwc/resourceLoader/resourceLoader.js
d3force-app/main/default/staticresources/
├── d3/
│ ├── d3.v5.min.js
│ └── style.css
└── d3.resource-meta.xml ← content type: application/zip
Source files: /Users/chris/Git-Repos/Extension Testing/lwc-recipes-main/force-app/main/default/staticresources/d3/
Copy the d3 static resource folder and its .resource-meta.xml from the recipes project into force-app/main/default/staticresources/.
resourceLoader utility LWCCopy resourceLoader.js (and create a minimal js-meta.xml for it) from the recipes project. This utility is imported in Step 4 instead of lightning/platformResourceLoader.
data.js with dummy network dataProduce a small, business-themed dataset (e.g., product categories and their relationships, or a mini account hierarchy). Format:
export default {
nodes: [{ id: 'Node A', group: 1 }, ...],
links: [{ source: 'Node A', target: 'Node B', value: 3 }, ...]
};
d3NetworkChart.jsKey patterns (adapted from libsD3 recipe):
loadScript from c/resourceLoader (LWR-safe, not lightning/platformResourceLoader)@salesforce/resourceUrl/d3./datarenderedCallback with d3Initialized guardinitializeD3() builds a force simulation with link, charge, and center forcesd3.drag()group using d3.scaleOrdinal(d3.schemeDark2)d3NetworkChart.html<template>
<div class="chart-container">
<svg class="d3" width="100%" height="100%" lwc:dom="manual"></svg>
</div>
</template>
d3NetworkChart.cssStyle links (stroke, opacity) and nodes (stroke, fill, cursor). Match Tableau Next’s neutral colour palette where possible.
d3NetworkChart.js-meta.xml<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>66.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>analytics__Dashboard</target>
</targets>
<targetConfigs>
<targetConfig targets="analytics__Dashboard">
<property name="chartTitle" type="String" label="Chart Title"
description="Title displayed above the network chart" />
</targetConfig>
</targetConfigs>
</LightningComponentBundle>
force-app/main/default → SFDX: Deploy This Source to Org (or sf project deploy start)d3NetworkChart should appear in the pickerviewBoxresourceLoader issue)When ready to replace dummy data with a real semantic model:
<hasStep>true</hasStep> to targetConfig in the metadata file — this enables binding to a dashboard step (dataset)POST /services/data/v59.0/semantic-engine/gateway@salesforce/apex or user session)CDP Semantic Query Permissions enabled on the orgSDK_EVENTS.FILTER_CHANGE so the chart re-queries when dashboard filters changeI’ve accepted and let it churn away… It summarised th changes as follows:
3 new additions to the SFDX project at force-app/main/default/:
d3NetworkChart LWC — the extension itself| File | Purpose |
|---|---|
d3NetworkChart.html |
Template with lwc:dom="manual" SVG; optional title via {chartTitle} |
d3NetworkChart.js |
D3 v5 force-directed simulation with draggable nodes, group colouring, and node labels |
d3NetworkChart.js-meta.xml |
analytics__Dashboard target — makes it visible in the Tableau Next extension picker |
d3NetworkChart.css |
Dark-themed styles matching Tableau Next’s aesthetic |
data.js |
15 business-named account nodes across 6 groups, with 24 relationship links |
resourceLoader utility LWCCopied from the recipes project — required workaround for LWR runtime, which blocks lightning/platformResourceLoader.
d3 static resourceD3 v5 minified JS + style CSS, required because Salesforce orgs cannot load scripts from external CDNs.
sf project deploy start --source-dir force-app)d3NetworkChart should appearI’m now deploying…
Some errors and back and forth with claude…
Successful deployment

In Tableau Next I can now open a dashboard, click add extension and my new extension appears! But when added I get:
This page has an error. You might just need to refresh it.
[NoErrorObjectAvailable] Script error.
But I just reloaded the page and it added okay! Well that was easy.
Now to make it more complicated…
Written on May 14th, 2026 by Chris Meardon