Ship a Meridian MMM Dashboard Your CFO Will Actually Open: The 4-Hour v0 + Claude Code Build

Article title in white and brand-green text on dark teal header about building a Meridian MMM dashboard.

Share This Post

TL;DR

  • Meridian’s raw outputs land as posterior samples, charts, and HTML reports. None of it is a budget-meeting artifact. The gap to a usable dashboard is a 4-hour v0 + Claude Code build, not a 6-week BI sprint.
  • Three components make the dashboard usable: a 50% credible-interval band on every response curve, a labeled saturation point where marginal ROI (return per extra dollar) crosses 1.0, and a current-spend overlay that drops a vertical line on each channel.
  • Render a 95% band and the dashboard looks useless. Render only the mean line and your CFO over-trusts a number that’s actually a fuzzy range.
  • Export your posterior draws (the model’s full set of plausible guesses) to a flat JSON before you touch v0. Computing percentiles in the browser is the failure mode that eats a Saturday.
  • Deploy private. Vercel password protection or Clerk takes 10 minutes. A public URL with client spend data is the audit problem you don’t want.

Questions this article answers:

Google’s open-source marketing mix model, Meridian, went generally available in January 2025 (per the Google Ads & Commerce blog). That’s the good news. The bad news is what lands in your folder when the model finishes: posterior samples in an ArviZ object, response-curve plots, an optimizer output, and a summary HTML file (see the post-modeling visualizations docs). None of it is something a CFO can read in a 20-minute budget meeting.

This is the practitioner walkthrough to turn Meridian MMM outputs into a marketer-friendly dashboard with v0, without waiting on a data team. Roughly four hours: 30 minutes to export the artifacts, 45 minutes to scaffold the UI in v0, two hours to wire the three components in Claude Code, and 30 minutes to deploy privately on Vercel.

The goal is not a prettier chart. The goal is a dashboard your finance team opens twice, and uses to move money.

What Meridian Actually Outputs After a Model Run

A Meridian run produces a Bayesian posterior, a set of plausible model results, not a single answer. The artifacts you’ll touch:

  1. An InferenceData object, the master container, typically saved to disk via to_netcdf() (ArviZ docs). It holds every posterior draw from every chain.
  2. Response curves, for each channel, a grid of spend levels and the modeled incremental revenue at each level. Every point is a distribution, not a number.
  3. Budget optimizer results, the optimized spend per channel for a given budget. Pull this into a flat file when you export.
  4. A summary HTML report, useful for the analyst, unreadable for the CFO.
  5. Matplotlib plots, Meridian’s default chart export. Skip these. You’ll re-render in the browser.

The InferenceData Object, Explained for Marketers

InferenceData is an ArviZ data structure that stores the model’s full set of guesses. Think of it as a spreadsheet where each row is one plausible model fit, and each column is a parameter (channel effect, saturation rate, ad-stock decay). Slice it to get the median, the spread, or any percentile you want.

For the dashboard, you don’t need to understand the internals. You need to know that the percentiles you’ll compute come from this object.

Why Response Curves Are the File That Matters Most

A response curve shows how revenue changes as spend on a channel changes. Meridian gives you one per channel. At every spend level it gives you the full posterior, often a few thousand draws across all chains depending on your sampler settings.

This is the conceptual key.

Key Concept: Plot the mean of those draws and you get a smooth, confident-looking line. Plot the spread and you see how uncertain the model actually is. A CFO who sees only the smooth line will treat the model as deterministic and over-anchor on the exact dollar. That’s how MMM dashboards stop getting opened.

Export Your Posterior Draws to This JSON Schema Before You Touch v0

Flatten Meridian’s posterior into one JSON file per model run, with one row per (channel, spend_level) pair. Computing percentiles client-side is slow, ugly, and the most common reason these builds stall.

Here’s the schema:

“`json { “model_version”: “2026-03-15”, “channels”: [ { “name”: “Brand Search”, “current_spend”: 42000, “saturation_point”: 38500, “spend_grid”: [0, 5000, 10000], “mean”: [0, 18200, 33100], “p25”: [0, 16400, 30200], “p75”: [0, 20100, 36400] } ] } “`

Six fields per channel. That’s it.

The Schema, Field by Field

  • spend_grid, the x-axis. Meridian generates this for you.
  • mean, the posterior mean revenue at each spend level. The headline line.
  • p25 and p75, the 25th and 75th percentiles. These bound the 50% credible interval (the shaded band).
  • saturation_point, one number per channel: the spend level where the next dollar returns roughly one dollar. Computed once at export.
  • current_spend, trailing 30-day spend per channel, pulled from your ad platforms or finance system.

Computing the Saturation Point Where mROI = 1.0

Marginal ROI is the revenue from the next dollar of spend. At the saturation point, that next dollar returns exactly one dollar. Anything beyond is unprofitable on the margin.

In Python, once you have the response curve mean as an array:

“`python import numpy as np

def saturation_point(spend_grid, mean_revenue): mroi = np.gradient(mean_revenue, spend_grid) idx = np.argmin(np.abs(mroi – 1.0)) return float(spend_grid[idx]) “`

Store one number per channel. The dashboard doesn’t recompute.

Why You Join Current Spend at Export, Not in the Dashboard

Current spend changes weekly. The model changes quarterly. Wire current spend into the dashboard layer and every spend refresh requires a redeploy. Bake trailing 30-day spend into the JSON at export, and you swap the JSON without touching the code. Same logic as keeping config out of source.

Portrait process-flow infographic in teal and green outlining steps to convert Meridian MMM outputs into a v0 dashboard.
The turn meridian mmm outputs into a marketer-friendly dashboard with v0 process, step by step.

Scaffold the Dashboard in v0 With This Prompt (and Know What It Will Get Wrong)

Use v0 to generate the Next.js + shadcn/ui scaffold. Be explicit about the three components or v0 will hand back a generic chart that hides uncertainty.

Here’s the prompt that works:

Build a Next.js 14 dashboard with shadcn/ui and Recharts that visualizes marketing mix model response curves. The data is a JSON file at /data/meridian.json with this schema: each channel has name, current_spend, saturation_point, spend_grid (array), mean (array), p25 (array), p75 (array).

>

For each channel, render a card containing: 1. A chart using ComposedChart with an Area between p25 and p75 (50% credible interval, light blue, 30% opacity), a Line for mean (solid blue, 2px stroke), a ReferenceLine (vertical, dashed) at saturation_point labeled “Saturation”, and a ReferenceLine (vertical, solid red) at current_spend labeled “Current”. 2. Above the chart: channel name, current spend (formatted as USD), and a badge that says either “Under saturation” or “Past saturation” based on whether current_spend is less than saturation_point.

>

Use the shadcn Card, Badge, and Slider primitives. Make the layout a responsive grid: one column on mobile, two on tablet, three on desktop.

Three Things v0 Will Hand Back Broken

v0 reliably fumbles three things on the first pass:

  1. It plots the mean alone. If you don’t explicitly say “area between p25 and p75,” v0 defaults to a single LineChart with no uncertainty.
  2. It picks the wrong percentiles. Say “credible interval” without a number, and v0 reaches for 95%. Too wide to be useful.
  3. It mis-binds the slider. Ask for a “spend slider” and v0 connects it to a number display, not to a vertical line on the chart.

Don’t fix these in v0. Export to your local repo and hand them to Claude Code.

Hand the v0 Project to Claude Code to Wire the Three-Component Pattern

The three-component pattern, band, saturation label, spend overlay, is what converts the dashboard from a data-science artifact into a budget-meeting tool. Skip one and it goes back in the drawer.

Download the v0 project, open it in Claude Code, and give it three sequential prompts.

Why 50%, Not 95%, Is the Right Band for a CFO

A 95% credible interval contains 95% of the posterior probability. For a typical MMM channel, that band is wide enough to span “this channel is your best” to “this channel does nothing.” Shown to a CFO, it kills the model’s credibility on contact.

A 50% band, the interquartile range, covers the middle half of plausible outcomes. Narrow enough to anchor a decision. Wide enough to convey honest uncertainty. The Bayesian framing exists precisely so the dashboard reflects what you actually know (see ArviZ on credible intervals).

Operator Note: Switching from a mean-only line to a 50% band tends to shrink the size of reallocation moves teams are willing to make. Wide-uncertainty channels stop attracting aggressive bets. That’s the point.

Claude Code prompt:

In the ResponseCurveChart component, replace the existing Area with a shaded band drawn from p25 to p75. Use fill hsl(217 91% 60%) with opacity 0.18, no stroke. Order the layers: Area first, then ReferenceLines, then the mean Line on top so the line sits above the band.

Labeling the Saturation Point So It Reads in One Glance

The saturation point earns its place only if the CFO sees it without squinting. A dashed vertical line plus an inline label beats a legend entry every time.

Claude Code prompt:

Add a ReferenceLine at x={saturation_point} with stroke="hsl(0 0% 40%)", strokeDasharray="4 4", and a Label positioned top reading “Saturation: $” + formatted dollar amount. Use Intl.NumberFormat to round to nearest $1,000.

Wiring the Spend-Overlay Slider to the Curve, Not to a Number

This is the moment the dashboard becomes useful. The slider drops a red vertical line on the response curve at the user’s chosen spend level. The badge above the chart updates in real time to read “projected revenue at this spend: $X.”

Claude Code prompt:

Add a shadcn Slider above each chart, min=0, max=2× current_spend, step=1000, defaultValue=current_spend. Wire its value to a second vertical ReferenceLine on the chart (solid red, 2px). Display projected revenue beside the slider: look up the mean array at the closest spend_grid index to the slider value, format as USD.

Leave the default at current_spend so the dashboard opens to the present state. Let the user drag from there.

Deploy Privately on Vercel With Auth, Don’t Ship Client Spend to a Public URL

This is internal data. The dashboard goes behind auth before it goes anywhere else.

Auth Options That Take 10 Minutes

Three options, in order of speed:

  1. Vercel password protection, one toggle in project settings, single shared password. Fine for a small finance team.
  2. Clerk, drop-in auth with email magic links. About 15 minutes to wire. Use this when more than three stakeholders need access.
  3. SSO via Clerk or WorkOS, for enterprise clients who require it. Roughly an hour of additional setup.

Store the Meridian JSON in a Vercel Blob or private S3 bucket. Don’t commit it to the repo. Strip client identifiers from filenames before upload.

Refresh Cadence: Quarterly vs. Monthly

Most accounts refresh the underlying Meridian model quarterly. Monthly refreshes make sense when you’re spending above $250k/month or in a seasonality-heavy category (retail in Q4, tax services in Q1, home services in spring).

The dashboard refresh is just a JSON swap. The schema is stable, so the UI doesn’t change. Version the JSON file (meridian-2026-q1.json) and let the dashboard read the latest by default with a dropdown to compare versions.

Total time: 30 minutes to export, 45 minutes in v0, two hours in Claude Code, 30 minutes to deploy. Four hours.

When Scenario Planner Is Enough, and When You Need to Own the Layer

Google’s Scenario Planner on top of Meridian is the path of least resistance, and for some teams it’s the right call. Build your own when Scenario Planner can’t carry the use case.

Scenario Planner Is Sufficient When You Check All Five

Condition Scenario Planner works
Single stakeholder, no agency relationship
Media mix is mostly Google channels
No custom branding required for board reporting
No offline, affiliate, or retail-media spend to overlay
Reporting lives inside Looker Studio and that’s fine

Check all five, stop reading, use Scenario Planner.

Build Your Own When Any of These Are True

  • You’re an agency reporting on multiple clients with separate logins.
  • A CFO, board, or investor wants the deck branded.
  • Your media mix includes non-Google spend (Meta, TikTok, affiliate, OOH, retail media) that needs to overlay on the curves.
  • You need to integrate spend data from a finance system, not just ad platforms.
  • You want to evolve the dashboard beyond Google’s roadmap, adding LTV overlays, contribution-margin views, or weekly attribution comparisons.

The v0 + Claude Code path exists because Scenario Planner stops where most working teams start. The same workflow extends to building agentic content QA pipelines or MCP servers for call disposition reporting. Once you can wire JSON to a UI with Claude Code, the cost of owning the layer drops fast.

For context on where MMM is heading across the industry, our marketing mix modeling at GML 2026 piece covers the broader rollout.

Related guides

Frequently Asked Questions

What does Meridian actually output after a model run?

Meridian produces an ArviZ InferenceData object containing posterior draws, response curves per channel, budget optimizer results, and an auto-generated summary HTML report with accompanying chart visualizations. For the dashboard build, you only need the InferenceData and the response curves. The rest is reference. Skip the default chart exports because you’ll re-render the charts in the browser with proper uncertainty bands.

Can a marketer with no engineering background really ship this in an afternoon?

Yes, if you’re comfortable running a Python script and willing to debug Claude Code output for two hours. The hard conceptual work is understanding that response curves are distributions, not lines. Once that clicks, the rest is configuration. The four-hour timeline assumes you can run a Jupyter notebook, paste prompts into v0 and Claude Code, and follow basic instructions in a terminal. If any of that sounds unfamiliar, add two to three hours, or bring in a developer for the export step.

Why a 50% credible interval and not 95%?

A 95% band on a typical MMM channel is wide enough to span “this is your best channel” to “this channel does nothing,” which makes the model look useless to a CFO. A 50% band, the middle half of plausible outcomes, is narrow enough to support a decision but wide enough to convey honest uncertainty. Moving from mean-only to a 50% band tempers the size of reallocation bets teams are willing to make, because channels with wide uncertainty stop attracting aggressive moves.

Is Google’s Scenario Planner enough on its own?

Scenario Planner is enough if you have a single stakeholder, a mostly-Google media mix, no custom branding needs, and no non-Google spend data to overlay. Build your own dashboard when you’re an agency with multiple clients, when finance wants branded reporting, when you need to overlay offline or affiliate spend, or when you want to evolve beyond Google’s roadmap. Most agencies and any team spending across more than three platforms outgrow Scenario Planner within a quarter.

How often should I refresh the Meridian model?

Quarterly refreshes work for most accounts. Move to monthly when you’re spending above $250k/month or in a category with heavy seasonality. The dashboard itself refreshes faster, since it’s just a JSON swap with no code change required. Version each export by date (meridian-2026-q1.json) so you can compare runs side by side and catch drift before it shows up in a budget meeting.

What’s the biggest rendering mistake that makes a Meridian dashboard misleading?

Plotting only the posterior mean as a smooth line. It turns a Bayesian model into a fake deterministic one and invites the CFO to over-anchor on an exact number. The fix is the 50% credible-interval band rendered as a shaded area beneath the mean line. Same data, but the spread is visible. Anyone making a budget decision off the dashboard sees the model’s confidence, not just its best guess.

If You’d Rather Have Someone Build the Asset With You

The four-hour build assumes you’re comfortable in a terminal, willing to read Claude Code’s output, and patient enough to debug a slider binding. Not every marketing leader signed up for that. That’s a fine reason to bring in help.

If you’d rather have us build the Meridian dashboard with you, or build the broader internal reporting stack your team relies on, book a free consultation. We’ll talk through what you’d own, what we’d build, and where the handoff makes sense. A build conversation, not a sales pitch.



Ready to put this into action?

Picture of SHANE MCINTYRE

SHANE MCINTYRE

Founder & Executive with a Background in Marketing and Technology | Director of Growth Marketing.