The .trex manifest: how Tableau finds your page

Write the one XML file that registers your page as an extension — and know the three lines you will ever need to change.

beginner7 min1 step

Why it matters

The manifest is the only genuinely Tableau-specific artifact in an extension, and it is where every first attempt dies: a wrong URL, a malformed icon, a permission you didn't declare. It is also tiny, and only three parts of it ever matter in practice: the source-location URL Tableau loads, the min-api-version you promise to support, and whether you ask for the 'full data' permission. Get those three right and the rest is boilerplate you generate once and copy forever. The .trex file is what you double-click, drag in, or publish — to Tableau, the manifest IS the extension.

Prompt Recipe

Generate a Tableau dashboard extension manifest saved as hello-dashboard.trex. Use manifest-version 0.1 with the http://www.tableau.com/xml/extension_manifest namespace, extension id com.tableauops.hello-dashboard, extension-version 0.1.0, min-api-version 1.10, and source-location http://localhost:58072/index.html. Include a name resource, description, author block, and a base64 PNG icon placeholder. Do not declare the full data permission — this extension reads only summary data. Then explain each element in one line and tell me which Tableau release min-api-version 1.10 corresponds to.

Everything except the marked decisions is copy-paste boilerplate. The source-location URL is where Tableau fetches your page — localhost while you develop, an HTTPS address when you ship, and the single most common reason a zone renders blank. The min-api-version is a promise about the oldest Tableau you support; 1.10 (Tableau 2022.4) is the sensible floor because it is where the modern data reader landed. The permissions block exists for one reason: reading underlying (row-level) data requires declaring full data, and viewers are told about it when they add your extension. Summary data — what this wall uses until you truly need rows — requires no permission at all.

The id uses reverse-DNS naming to be globally unique. It is how Tableau Server safelists your extension later, so pick a real one now — renaming it after dashboards depend on it is a migration.

xml
<?xml version="1.0" encoding="utf-8"?>
<manifest manifest-version="0.1"
          xmlns="http://www.tableau.com/xml/extension_manifest">
  <dashboard-extension id="com.tableauops.hello-dashboard"
                       extension-version="0.1.0">
    <default-locale>en_US</default-locale>
    <name resource-id="name"/>
    <description>Says hello to the dashboard it lives in</description>
    <author name="Your Name" email="you@example.com"
            organization="TableauOps" website="https://tableauops.com"/>

    <!-- Decision 2: the oldest Tableau you promise to support.
         1.10 = Tableau 2022.4, the floor for the data reader. -->
    <min-api-version>1.10</min-api-version>

    <!-- Decision 1: the page Tableau loads into the zone. -->
    <source-location>
      <url>http://localhost:58072/index.html</url>
    </source-location>

    <!-- Any 70x70 PNG, base64-encoded. This one is a single gray pixel. -->
    <icon>iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==</icon>

    <!-- Decision 3: permissions. Summary data needs none — declare
         "full data" ONLY if you call the underlying-data methods:
    <permissions>
      <permission>full data</permission>
    </permissions>
    -->
  </dashboard-extension>
  <resources>
    <resource id="name">
      <text locale="en_US">Hello, Dashboard</text>
    </resource>
  </resources>
</manifest>

Got what you came for? Mark the stop and the line fills in beneath you.