How to build a marketing analytics dashboard with Replit
The dashboard and the thing hosting it end up in one workspace. An import that validates every row and keeps its refusals, six rates with a guard on each division, a widget library people compose boards from, and three roles, all described to the Agent, with Postgres appearing beside it and a working address waiting at the end.
Replit
$ Build a marketing analytics dashboard: import campaign CSVs with every row validated and every refusal kept, compute click-through rate, cost per click, cost per thousand, cost per acquisition and both returns with guarded division, give me a library of charts, metric cards and tables to compose boards from, four board-wide filters, and three roles with per-dashboard sharing, with a Postgres database in this workspace.
- Postgres provisioned in the workspace
- Real export imported and checked
- Ready for you to review
What a marketing analytics dashboard actually is
One screen that answers "is this working". The difficulty is never the chart, but that the answer has to be assembled from six platforms that each define the question differently, and then defended to somebody about to move a budget.
Every platform you advertise on already has a dashboard, and each one is confident. The trouble is that they disagree: the ad network counts a conversion when somebody clicked within thirty days, your analytics counts it when they landed and did something, your shop counts it when the payment cleared, and none of the three is lying. A marketing analytics dashboard is where you decide which of those you mean and then apply that decision to everything, so a number on one card can be compared with a number on another.
Next comes the plumbing, which is where the project lives or dies. Numbers arrive as exports: a CSV per platform, per account, per month, with column names that move and campaign names that contain commas. Something has to read those files, check every row against the shape you expect, load what passes, and keep what fails somewhere you can look at it. That last part is the difference between a dashboard people act on and one they quietly check against a spreadsheet.
Only then do you get to the rates, and they are simple arithmetic with one trap in each: cost per acquisition when there were no acquisitions, click-through rate when there were no impressions, return on ad spend on a day nobody spent. Guard every division and the six rates fall out in an afternoon. Skip that and your dashboard shows infinity to the person deciding next quarter’s budget.
Agree Your Definitions Before You Build
Two people looking at the same campaign will disagree about how it did, and almost always because they are counting different things rather than counting wrongly. What counts as a conversion, whether revenue is gross or net of refunds, whether a view-through counts at all, which currency, which time zone a day ends in: write those down before anything is built. They are the specification, and a dashboard that has not settled them is a machine for producing arguments faster.
Reliable Imports From Messy CSV Exports
A dashboard reading a clean table is a weekend. A dashboard reading whatever six ad platforms exported is real software: headers that change without notice, a campaign named "Spring Sale, EU" that shifts every column after it, blank rows where somebody left a total, the same day uploaded twice. Deciding what happens to those rows (kept with a reason and a line number, rather than absent from a chart) is what a marketer checks first, because the first question about any total is always whether it includes everything.
Flexible User Roles & Custom Permissions
This is the part most reporting builds discover late. A marketing dashboard is read by people who must not be able to change it, built by people who should not necessarily see every client, and administered by somebody who needs both. That is two separate questions (what may this person do, and which numbers may they do it to) and answering only the first is the most common way a reporting tool ends up showing one client another client’s spend.
The part companies rate themselves worst at
4.0 / 7
is how 308 senior marketers scored their own company at "integrating marketing technologies across other data systems in our company", the lowest integration score on the list, and identical to the 4.0 they gave it two years earlier. The same people scored themselves 4.9 at choosing which vendor to buy from. That gap is this whole project: acquiring the tool is the part this profession has got good at, and joining the numbers up is the part that has not moved in two years. Published April 2026 by The CMO Survey, in the field that January, and sponsored by Duke University’s Fuqua School of Business, Deloitte and the American Marketing Association. Deloitte sells services next door to this finding, though the report says sponsors get neither the data nor the participant list.
The CMO Survey, 35th edition (n=308, 97% VP-level or above), fielded January 2026 · checked fielded January 2026
The parts every marketing dashboard is built from
One of these six is the charts. The other five are where the build time goes, and where the credibility comes from.
CSV Import That Keeps Every Rejected Row
Every platform hands you a CSV and every CSV will disappoint you: a header renamed since last quarter, a campaign called "Spring Sale, EU" that pushes every later column one place along, the same fortnight uploaded twice by two people. Coping with that is half the job. The half that decides whether anybody trusts the dashboard is what happens to the rows that fail: written to a rejects table with the line number and the reason, so a total is never quietly short. Then a re-upload has to be a decision rather than an accident: keyed on the day, the channel and the campaign, and either skipped or allowed to overwrite, because somebody will upload March twice.
Six Marketing Metrics Calculated Automatically
Click-through rate, cost per click, cost per thousand impressions, cost per acquisition, return on ad spend, return on investment. All six are one line of arithmetic and all six divide by something that is sometimes zero: a day with no impressions, a campaign with no conversions, a channel nobody spent on. Compute them in one place with a guard rather than in each widget and you get two things: no infinity on a card in front of a client, and one definition to change when your team decides revenue means net of refunds after all.
Build Your Own Dashboards From a Widget Library
The dashboard you design in week one is not the one anybody wants in month three. Ship a small library instead (a line and an area chart for trends, a bar chart for comparing channels, a pie for a split, a metric card for a single number, a table for the detail underneath) and let people compose their own board out of it. Two consequences to design for: each widget carries its own configuration, and the layout is saved as data rather than code, because whoever arranges it will not be whoever built the app.
Every Chart Respects the Viewer’s Permissions
Once a board is composed rather than designed, the widget has to be able to answer its own question, and that answer must depend on who is asking. The instinct is to run those queries with an all-powerful server key and filter afterwards. Resist it: the function that answers a widget’s question should carry the viewer’s own credentials, so the database applies their permissions rather than the code remembering to. It is no more work up front, and it is what stops one client’s spend surfacing on another’s board the day somebody duplicates a dashboard.
Dashboard-Wide Filters
A date range, a channel, a campaign and a country, and every widget on the board has to respect all four, including the ones added after the filters were written. Set your date and campaign filters once at the top of the page, and every chart on your dashboard automatically updates in perfect sync, no complex setup required. Remember the selection between visits too: whoever opens this at nine on Monday had those same four filters set on Friday.
Control Who Can Edit vs. Who Can Only View
A viewer reads. An editor builds dashboards. An administrator manages people. That is the first question. The second, and the one that gets forgotten, is which numbers they may do it to: a freelancer building boards for four clients should see four, not nine. Answer both separately (a role, and a grant on one dashboard) and the awkward cases stop needing new code. Answer only the first, and every awkward case becomes a special rule in a screen.
Own the dashboard or rent one per client
Every product below is a monthly subscription, and the bill grows with the business it reports on. Owning the dashboard is a single purchase: the code is yours, and nothing about it is metered.
Build your own
Monthly SaaS tools trap you with recurring fees, per-seat licenses, and paywalled features. With Remixable, you pay once and own the complete, production-ready codebase forever. Launch instantly on day one, not weeks later, and add unlimited dashboards and clients without ever being forced to upgrade.
- A fifth or fiftieth client is a row in a table rather than a line on an invoice
- An eleventh dashboard is an afternoon for whoever wanted it, not an upgrade
- Every imported row is in your own database, queryable without asking anybody for an export
- What counts as a conversion is your definition to write down and change
- No tier gets restructured under you, and nothing moves behind an upgrade you did not agree to
Rent the reporting layer
AgencyAnalytics · DashThis · Supermetrics · Funnel · Data StudioWhat renting buys is the part this template deliberately does not attempt. Live authenticated connections into the ad platforms, so nobody exports a file. Somebody else’s problem when a platform changes its API. Hundreds of pre-built connectors. White-label client portals. At the top end, a data warehouse with your marketing history modelled in it.
- Authenticated connectors into the ad platforms, refreshed on a schedule. This template reads a CSV you give it
- Between 85 and 600-plus integrations already written, tested and maintained
- White-label client portals and branded reporting, which agencies are genuinely bought for
- Warehouse destinations and modelled marketing data at the enterprise end
- Four of the five advertise unlimited users, so what you are billed for is clients, dashboards, data feeds or licences
First because it states the meter more plainly than anybody else here: you buy clients. Its own feature list reads "Unlimited data sources", "Unlimited reports & dashboards" and "Unlimited staff & client users", which is the argument of this whole table in three lines, since the thing being rationed is the client, not the person. 14-day trial, no card.
agencyanalytics.com · checked August 2026
Here because the ladder is how many dashboards you may keep, which is the strangest meter to explain to somebody about to build their own. Users are unlimited on all four tiers. What you climb the ladder for is permission to have more boards. Hold that beside a build you own, where an eleventh dashboard is a row in a table. One honesty note: the page’s monthly and annual figures came back identical to our reader, so the figures above are the ones it lists plus the annual saving it advertises. We have not invented an annual per-month price.
dashthis.com · checked August 2026
The most transparent row in the set, and the one that meters most things at once: data sources, destinations and users all move together, with both billing bases published. Its page says pricing is based on "the number of data sources and destinations you connect" and that it does "not charge usage fees beyond your agreed-upon license subscription fee". Do the count before comparing: search, social, email, your shop and your analytics is five sources before anybody mentions a second ad account, and Starter allows three. Quoted in euros because that is the currency its page served us.
supermetrics.com · checked August 2026
The enterprise end of the same job, included for what it does not tell you. Both figures are floors rather than prices, nothing on the page says per what, and Business lists "Unlimited users and workspaces", so once again the seat is not the product. What you get for it is scale: 113 connectors and 13 destinations at Starter, 579 and 46 at Business, which is both the honest case for renting and the reason the floor is $300.
funnel.io · checked August 2026
Last because it is the real default: most marketing dashboards that exist are free reports in Google’s own tool, and an honest comparison starts by admitting that. The paid tier is worth knowing for its shape, quoted from Google’s own docs: "A self-service Data Studio Pro subscription is based on individual user licenses", you are "billed monthly for the number of Pro licenses in the subscription, regardless of whether or not those licenses are used", and "Every Data Studio Pro subscription is associated with one (and only one) Google Cloud project". Note the name, too. Almost everybody still says Looker Studio, and the documentation now reads "Looker Studio is now called Data Studio." The licensing shape is the part worth remembering.
docs.cloud.google.com · checked August 2026
Rule of thumb: if the numbers must arrive without anybody exporting a file, if you need forty connectors maintained by somebody else, if clients expect a branded portal, or if a platform’s API changing is a problem you want to buy your way out of, rent, and if one free report in Google’s own tool already answers your question, use it and spend the fortnight on something else. Build when the meter is the problem: when the fifth client, the eleventh dashboard or the fourth data feed is the thing standing between you and looking at your own numbers, and when what a conversion means in your business is a definition you would rather own than accept. The survey figure above is the honest reason this is worth the effort: buying another tool is the thing this profession is already good at, and it has not moved the integration score in two years.
Why build with Replit
A real app needs somewhere durable to keep what people enter, rules about who’s allowed to see it, and a place to actually run once it’s built. Those are three separate problems, and most solo builds solve them badly, or not at all.
Replit’s Agent handles all three from one chat, in the same workspace the app ends up living in:
Describe
Tell the Agent what to build, in plain language.
Watch
It writes the code, sets up the database, and shows the app running live.
Try it
Use the real app in the preview rather than a mockup.
Publish
Take it live on Replit’s own hosting, or ask for the next change.
The build and the place it ends up running are the same workspace throughout, so there’s no separate hosting account to set up later.
One workspacebuilds, runs, and hosts it
Replit is the one tool here that also deploys what it builds. Publishing takes the same project live on Replit’s own infrastructure, with a working domain, uptime monitoring, and security scanning included.
Managed Postgres with 20GB included free
Ask the Agent to add a database and it creates the schema and wires your app to it. What you get is a real, fully-managed SQL database rather than a mocked one.
Up to 10 Agent sessions in parallel (Pro)
Core allows up to 2 parallel Agent sessions and Pro allows up to 10, so more than one part of the app can be worked on at the same time.
Pay a developer, or do it with AI
The one running cost worth noticing is the model behind the written summary, and it is smaller than people expect. The rest is a database plan and hosting, and both start free.
Hire a developer
Custom build, from scratch- Developer
- ~$11k-$43k
- Supabase (backend)
- Free tier · $25/mo (Pro plan)*
- Hosting
- $0 free tier
- AI model (the written summary)
- Usage-based - cents, not dollars
- Build time
- ~215 hrs of their work
~$11k-$43k to build, then from $25/mo plus a few cents of model usage
That range is our own ~215-hour estimate priced against the rate survey linked below, which puts senior US developers at $100-$150+ an hour, specialists higher, and agencies 20-40% above the freelancers they bid against. Hence $50/hr and $200/hr as the two honest ends. Where somebody has quoted you for "a marketing dashboard", interrogate the assumption rather than the total: what do they expect to receive from each ad platform, and what will they do with the rows that fail to load? Those two answers are usually the difference between the quote and the invoice.
Build it with Replit
From scratch, with Replit- Replit
- Free (daily credits) to $25/month (Core) or $100/month (Pro)
- Database (built-in Postgres)
- Free to start · 20GB included
- Hosting (Replit Deployments)
- Billed separately, on top of the plan
- Your time
- ~103 hrs
Free to try the idea, ~$25-$100/month on Core or Pro while you build a real one, then whichever plan (plus any deployment cost) you keep using
Replit’s plan price and its credit grant are the same number, not a subscription plus a separate credit purchase: Core is $25/month for $25 of monthly credits (or $20/month billed annually), Pro is $100/month for $100 of monthly credits (or $95/month annually). Once you publish, Replit bills hosting through its own Deployments separately, on top of whichever plan you’re on. Budget for it as a second line, not folded into the $25 or $100.
* The free-tier line that matters here is not the 500 MB of database, which holds years of campaign rows because this data is small and narrow. It is the pause: a free project stops after a week of inactivity, and a dashboard that gets read at the end of each month is exactly the project that goes quiet for a week and greets somebody with an error. Pro, from $25/mo, ends the pause, keeps 7 days of backups, and raises the allowances that matter once people are downloading board exports: 250 GB of egress (then $0.09 per GB) and 100 GB of file storage (then $0.0213 per GB), the latter being where your uploaded CSVs pile up.
Either way the dashboard is yours, and the only running costs are a database plan and a few cents of model usage a month. What differs is who builds it: a developer you pay up front, or you, describing each piece to an AI tool.
Prices and rates from supabase.com, developex.com, developers.openai.com and replit.com, checked August 2026.
Decide before you build
Six things to write down before anything is built. Every one of them is a definition somebody will argue with later, and every one is cheaper to settle now than after a number has gone out in a client report.
What a conversion is, in one sentence
Write it down and make everyone who will read the dashboard agree to it. Does a view-through count. Does a repeat purchase in a week count once or twice. Is revenue gross or net of refunds. Which platform wins when two disagree, because they will. It decides what your import stores and whether two cards on one board can honestly be compared.
What your exports actually look like
Download a real month from each platform you advertise on and open the files. Their headers, their date formats, whether they quote fields containing commas, whether they append a totals row. That is the actual specification for the import. If the answer is five platforms, decide whether each gets its own shape or they normalise into one.
Wide rows, or facts and dimensions
Both shapes work. One wide table per day, channel and campaign is simple, fast to query, and awkward to extend. Separate fact tables around a campaign dimension take longer to build and absorb a new metric without migrating every screen. Pick deliberately. Wanting both means keeping two things in step.
Who builds, who reads, and which numbers
Three roles is the useful minimum: somebody who administers people, somebody who builds dashboards, and somebody who may only read what was shared with them. Then answer which dashboards each person may reach, the question that decides whether this is safe to hand to clients. With several clients, the rule that one never sees another belongs in the first migration.
What the written summary is allowed to say
A paragraph of plain English over a board is genuinely useful and the fastest thing here to get wrong. Decide that the model may describe only numbers it was given, and that any trend is computed by a query first. Handed this month’s figures alone, it will cheerfully say they are up on last month, and sound certain.
Whether anybody outside your company will see it
A board for your own team and a board for a client are the same code and different products. If clients will see it, two things move from later to now: their branding on it, and a share that grants one dashboard rather than an account. Retrofitting that is the most common rebuild in this category.
Comparing your build options
The same product three ways below, and the difference is how much of your time the invisible half takes: a parser that survives your real export, six guarded divisions, and rules about who may change a board. By hand, on a UI kit with nothing behind the cards, or through the Agent in a workspace that also ends up hosting the result.
The charts are a fortnight, and they are the part you can picture. The quarter goes on everything you cannot: getting six platforms’ exports to agree on what a conversion is, a parser that survives a campaign name with a comma in it, deciding what happens to the two hundred rows that will not load, a query engine behind widgets somebody else assembled in an order you never anticipated, and a permission model where the person who may build a dashboard is not the person who may see the numbers on it. None of that is visible on screen, and all of it decides whether the screen is worth reading.
A kit is unusually good value on this particular product: dashboard shells, KPI cards, a chart library and a drag-to-arrange grid are exactly what kits are made of, so more of the surface arrives free here than on almost anything else you could build. What arrives with it is nothing. No kit knows what a channel is, that cost-per-acquisition needs a guard against dividing by zero, which of your uploaded rows were rejected and why, or that this viewer may read one dashboard out of nine. Every number the cards display is still yours to define, compute and defend.
One request at a time to the Agent, with the Postgres, the server and every screen written inside the workspace that ends up hosting them. Two jobs land on your own server here that sit elsewhere on the other five tools: telling the database who is asking before any rule can act, and running an import on a timer once dropping a file in by hand each Monday stops being charming.
Estimate your exact build timeframe
Plenty of marketing teams need less than this. If nobody but you will ever sign in, or one fixed board beats a library, untick those rows and watch the estimate fall.
Your estimate
103 hrs
start to finish
Based on the 6 of 6 features you’ve selected, plus ~26h of groundwork. Toggle any on the left to watch the number move, and open the groundwork row to untick what you have already, such as a database that is already running or going live if you are only building a mock-up for now.
A rough estimate, not a quote. Real time depends on how much you customize and how clean your data is.
Let’s set up the tools you need
Replit runs entirely in the browser, and it’s the one tool here that also hosts what you build, so no GitHub account is required first. Before step 01: an account and a plan. A database comes later, the moment your app actually needs one, and GitHub whenever you want a copy of the code outside Replit.
Replit account
Sign up and you land in a workspace with an Agent chat, the code, and a live preview side by side, with nothing to install.
Sign up for ReplitReplit subscription
Starter’s free daily credits are enough to try an idea, not to finish one. Core is $25/month billed monthly, or $20/month billed annually, for $25 of monthly credits and up to 2 parallel Agent sessions. Pro is $100/month monthly, or $95/month annually, for $100 of monthly credits, more collaborators, and access to the strongest available models. The price you pay and the credits you get are the same number on both plans, so you have no separate subscription-plus-credits split to work out.
Compare Replit plansReplit database (Postgres)
Every Replit app includes its own managed Postgres database with 20GB of free storage. Ask the Agent to add one and it creates the schema and connects your app to it, with no separate account to create anywhere else.
Replit’s built-in database, Replit docsGitHub connection
Not needed to start, and not needed as an undo either, because Replit checkpoints the whole workspace as the Agent works. Connect a repository from the Git pane, free on every plan, and a copy of the real code lives outside Replit under your own account. Worth doing once the project is one you would hate to lose.
Using the Git pane, Replit docsThe first two are all you need to start. Everything here stays inside the one browser tab, the app included once you publish it, and the GitHub copy is the one deliberate exception.
Build your marketing dashboard, prompt by prompt
Six requests to the Agent, with the database and the hosting appearing in the same workspace as you go. One difference from the other five guides is worth knowing up front: because no hosted identity service exists here, your own server is what tells the database who is asking, which makes step 05 the step that carries the most weight.
- 01
A workspace, a database, and two rules written down
An app running with Postgres beside it, and one request that fixes the definitions everything after resolves to.
PromptSet up the projectCreate a React 18 + Vite + TypeScript app with Tailwind for a marketing analytics dashboard, add a Postgres database to this workspace, and get it running with the connection string in Secrets rather than anywhere the browser can read. Then fix the vocabulary before we build anything: organisation, data source, channel, campaign, impression, click, conversion, cost, revenue, dashboard, widget, reject. Two rules to hold throughout, written into the project’s notes rather than only agreed here. First: a conversion means exactly one thing across this whole project, written out in a sentence I will give you, and no screen ever computes a rate for itself. Every rate comes from one place that guards its division, so a card can never show infinity. Second: every row belongs to an organisation, every access rule has to test both what the person may do and which organisation the row belongs to, and neither half is ever left out because the other one looks sufficient.
Check where the connection string ended up before you go further. It belongs in Secrets and nowhere else (not in a file the browser downloads and not in a repository) and this is the one moment in the build where that is a ten-second check rather than an incident.
- 02
The schema, in the Postgres that just appeared
Describe the tables with an organisation on every row from the first migration. No sign-in and no screens yet, which makes this the cheap moment to discover your own exports carry something nobody planned for.
PromptModel the organisation and the campaign dataWrite the data model as migrations against the workspace Postgres, with no authentication yet and an organisation reference on every table from the start. Organisations. Members linking an account to an organisation. Data sources, one row per file or platform somebody imports from, carrying the import’s status and counts. Then the campaign data in two shapes so I can choose later: a wide table of one row per date, channel and campaign with impressions, clicks, conversions, cost and revenue on it, and alongside it a campaign dimension with separate fact tables for sessions, cost and conversions. Then a rejects table holding every row an import refused, with the line number, the raw row and the reason. Then dashboards, and widgets belonging to a dashboard carrying their own type, title, configuration and position. Seed two organisations with deliberately awkward data: one with a campaign whose name contains a comma, one with the same fortnight imported twice.
This is ordinary Postgres, which is the useful thing to remember all the way through: the migrations here, and the access policies in step 05, work exactly the way they would on any other Postgres host. What is different is only where identity comes from, and that is the next-but-one step.
- 03
A real export, imported by your own server
Get a real month out of one ad platform and put it in the workspace. Here the import runs on the same server that hosts the app, which makes this a good moment to decide it should eventually run on a timer too.
PromptImport a real exportA real CSV export from my ad platform is in this workspace. Read it before you write anything and tell me what you found: the exact column names, the date format, whether fields containing commas are quoted, how it writes a zero or a missing value, any totals row at the bottom, and anything you cannot map onto my schema. Then build the import: object storage for uploaded files, private and reachable only through my own server, restricted to CSV and scoped per person, plus a server endpoint that parses an upload with a parser that handles quoted fields properly rather than splitting on commas, checks the required columns are present, validates every row (dates parseable and inside a period I state, numbers numeric, nothing negative that cannot be, channel recognised), inserts what passes and writes every failure into the rejects table with its line number and reason. Never drop a row silently. Handle a re-upload as a decision: key each row on date, channel and campaign, and let me choose per import whether an existing row is skipped or overwritten. Make the whole thing safe to run twice with the same file, because it will be. Then import my real file and show me the accepted count, the full reject list with reasons, and the total spend of the accepted rows so I can compare it with the same figure in the ad platform.
The instruction about running twice safely matters more on this tool than the others, because the workspace makes it easy to put this import on a timer later, and the difference between an import that is safe to repeat and one that is not only shows up once nobody is watching it run.
- 04
Six rates once, then a library your own server answers for
The arithmetic in one place, then the widgets over it, and a single query endpoint behind them that answers according to who is asking.
PromptBuild the rates, the widgets and the filtersFirst the six rates, computed in one place and never in a component: click-through rate, cost per click, cost per thousand impressions, cost per acquisition, return on ad spend, and return on investment. Every one of them guards its division so a day with no impressions or a campaign with no conversions returns nothing rather than infinity, and I want to see what each returns for a zero case before we go on. Then the widget library: a line chart, an area chart, a bar chart, a pie chart, a metric card showing one number, and a data table. Each widget stores its own metric, grouping and title, and a dashboard stores the widgets on it and their positions as data so somebody can rearrange a board without a developer. Then one server endpoint that answers a widget’s query, taking the widget’s stored metric and grouping plus the board’s filters, and have it resolve the caller from the session and apply their permissions to the query, rather than querying as the database owner and filtering afterwards. Then four filters that apply to the whole board at once (date range, channel, campaign and country) passed into every widget’s query rather than filtering in the chart, and remembered between visits.
Note the wording about the query endpoint, which differs from the other five guides for a real reason: with no hosted identity service handing the database an account, filtering by the caller is something your own server has to do deliberately. Getting that one endpoint right is what makes every widget added later safe by default.
- 05
Sign-in your server owns, three roles, and both halves of every rule
The step carrying the most weight on this tool. Sessions live on your own server, so the announcement of who is asking (and which organisation they belong to) is code you own rather than something a service does for you.
PromptAdd auth, roles, and the access rulesAdd email-and-password sign-in owned by my own server: sign-up, login, logout, a session, and a profile row per account. Joining an organisation happens on the way in, and a person cannot choose or change which organisation they are in afterwards. Then have the server announce to the database, on every request, which account is asking and which organisation it belongs to, and write every access policy to read that announcement. Add a role enum of 'viewer', 'editor' and 'admin' in a roles table kept out of the account’s own record, plus a SECURITY DEFINER function that reads it, and give roles an organisation, so somebody is an editor of one organisation rather than an editor everywhere. Then row-level security on every table, and write every policy as two clauses joined: what this role is permitted to do, AND that the row belongs to the organisation this person belongs to. Never let a role check stand alone as if it were a boundary. On top of that, a per-dashboard grant table so a viewer can be given one specific dashboard, checked alongside the role rather than instead of it, and reachable from an invitation so the grant can exist before the account does and transfer when it is accepted. Then prove six things by querying as a signed-in account: the second organisation’s campaign rows are unreachable, its dashboards are unreachable, an editor of one organisation cannot update a dashboard belonging to the other, an admin of one cannot grant themselves access to the other’s dashboard, a viewer cannot create a widget, and a viewer cannot open a dashboard nobody shared with them.
The announcement is the line to check by hand. If the server ever fails to make it, the policies do not complain. They simply stop distinguishing between people, and what they stop distinguishing is one organisation’s campaign spend from another’s. Test it by removing the announcement deliberately once and watching what the second organisation can suddenly see.
- 06Destination
The summary, a ceiling, and one channel reconciled
The written paragraph at the top of the board, the cap on its spending, the exports, and the check against the platform the numbers came from, then publish, which on this tool is the same workspace.
PromptAdd the summary, the cap, and reconcileAdd the written summary at the top of a board, built so it cannot invent anything. Compute first, with queries: this period’s totals for each metric, the same totals for the previous period of equal length, the change in each, and the same per channel and per campaign. Send only those computed figures to one server-side function that calls the model, with its provider key in Secrets, and instruct it to describe what it was given without inventing a comparison, trend, projection or cause that is not in the numbers, and to say when a change is too small to matter. Print the figures it was given underneath and name the two periods. Then cap it before I ship: record every call with the account, the feature and the tokens used, and enforce a per-account hourly limit and a project-wide monthly limit I can change without redeploying, refusing politely when either is reached. Then exports: the board as a PDF, and the rows behind any single widget as CSV. Finally, reconcile with me: take one channel for one month, put your figures beside the same figures in the ad platform they came from, and account for every difference, including the rows sitting in the rejects table.
Once the reconciliation is clean, publish, and consider publishing the import on a timer as its own scheduled release while you are there. A dashboard is only as current as its last upload, and the difference between a tool people check and a tool people forget is usually whether somebody has to remember to feed it.
Ironclad role security and dashboard access control
Marketing dashboards hold sensitive financial and performance data. Here is how your system manages user permissions, isolates company data, and securely shares individual reports. On this tool a third requirement matters, because the only thing able to tell the database who is asking is your own server.
Logins your server owns
Sign-in is built into the app rather than delegated to a hosted identity service, so sessions live on your own server. Add one thing to that on this application: put a second factor in front of any account that can import data or edit a board other people read. The account that can quietly re-upload last month is worth more protection than a password.
Where the checking actually happens
What Replit provisions is ordinary Postgres, so its policies behave the way Postgres policies always behave. What differs is the source of identity. Nothing hosted is handing the database an account here, so have your own server announce who is asking (and which organisation they belong to) before each request touches a table, then write every policy to read that announcement. Miss it and the policies do not complain. They admit everybody, and what they admit everybody to is every organisation’s campaign spend in the workspace.
Roles live in their own table, checked by something that outranks the caller
The tempting shortcut is a role column on the account’s own record, which hands every user the power to promote themselves in one edit. Keep roles in a separate table and have your policies call a small function that reads it, one running with the database’s own authority rather than the caller’s. Three roles is the shape this product wants: an administrator who manages people, an editor who builds boards, and a viewer who reads.
A role is not a boundary: every rule needs both halves
The rule to carry away from this section. A policy saying "an editor may update a dashboard" answers what this person may do and says nothing about whose dashboard it is. Both halves belong in the same rule: the role permits the action, AND the row belongs to the organisation this person belongs to. Read each policy back asking which organisation it tests. If the answer is none, what you have written is a permission, not a boundary.
Then the second axis: a grant on one dashboard
Roles alone cannot express the case this product meets immediately: somebody who should see exactly one board out of nine. That wants its own table: a row saying this account may read that dashboard, checked alongside the role rather than instead of it, so access is the union of the two. It also has to work before the account exists, since people are invited by email and accept later, which means the grant hangs off the invitation and transfers when it is accepted.
What a row may become, not just which rows you may touch
A rule letting somebody edit their own record still has to say what that record may turn into. If the column deciding which organisation somebody belongs to is editable by them, every rule resolving through it is decoration. The same applies to joining: a rule permitting somebody to add themselves to an organisation has to constrain which organisation, or it is not a rule at all.
83 live policies, because every table needs its own answer
The migrations carry 98 row-level security statements, 15 of which were later replaced with a different rule rather than added to, so 83 policies are live over 24 tables. The number is structural rather than decorative: the campaign tables, the dashboards, the widgets, the data sources, the rejects, the invitations and the grant table each need separate answers for reading and for writing, and 15 of the live ones govern uploaded files. It is also the honest measure of why this part of the build takes a fifth of the estimate.
Some work has to outrank the person who asked for it
Parsing an upload and sending an invitation both touch more rows than the person triggering them should be able to write directly. That is server-side work carrying its own rule above the access policies, and each such job has to check the caller’s role in its own code, because nothing else will. The database connection it runs with belongs in Secrets, never in anything the browser downloads.
Keep the rows you rejected
When the import refuses a row, the refusal is data. Write those to their own table with the reason and the line number, because the alternative is silence, and a chart quietly missing two hundred rows is a wrong number presented with total confidence. A visible reject list is how a dashboard earns the right to be trusted, and it is the first thing to check after any upload.
Where the model key lives, and what the summary may see
The provider key goes in Secrets and nowhere else, for a plain reason: whoever holds it can spend your money. Then the question people skip. Decide what is allowed to leave. Period totals are one thing. A campaign list with what each cost is another. Send the smallest summary that answers the question, and put a per-account and per-month ceiling on how often anybody may ask for it, because requiring a signed-in user is a floor rather than a limit.
Backups, and how far back you can go
The Agent checkpoints as it works (files, configuration and optionally the database) so a bad change is a click back rather than an afternoon. Pro adds a 28-day database rollback on top. Worth having before the first month anybody reports from, because the imported rows can be re-uploaded from the platform and the boards somebody spent an afternoon arranging cannot.
One rule outranks the rest here: the database connection string and any model provider key live in Replit’s Secrets and nowhere else: not in anything the browser downloads, and not in a repository. Treat either one that escapes as burned, and replace it the same day.
What speeds the build, and what slows it
Speeds the build
- One small, specific request per message, checked in the live preview before the next one
- Letting the Agent provision the database from the chat instead of wiring one up by hand
- Running two Agent sessions in parallel on unrelated parts of the app, once your plan allows it
- Rolling back to a checkpoint the moment a change goes wrong, instead of unpicking it by hand
- Reviewing what Publishing changed, meaning the domain, who can reach the app, and the machine it runs on, before the first release
Slows the build
- Asking for the whole app in one message instead of one piece at a time
- Building screens for data that isn’t in the database yet
- Running unrelated Agent sessions against the same files at the same time
- Letting several risky changes stack up before checking whether any of them actually broke something
- Publishing without checking who the app is visible to first
Connecting GitHub (Optional)
Every change in Replit is saved automatically without any technical setup. Checkpoints are the day-to-day undo. You only need to link GitHub if you want a private copy under your own control or plan to hand the codebase over to external developers.
Every milestone is already saved
Replit’s Agent creates a checkpoint automatically at key points as it works: a full snapshot of the files, the configuration, and even the AI conversation itself, not just the code.
Checkpoints and rollbacks, Replit docsRolling back restores the whole workspace
One click returns your project to an earlier checkpoint (files and configuration together, and optionally the database), which is broader than a typical code-only undo, so a rollback after real data has changed is worth a second look before you confirm it.
GitHub keeps a copy outside Replit
Connect a repository from the Git pane, free on every plan, and stage, commit, and push changes back to GitHub with a click, or pull in anything changed outside Replit.
Using the Git pane, Replit docsIt’s also how an existing project gets in
Point Replit at a public repository’s URL for a fast import, or use the guided import for a private one. Either way, Replit detects the stack and installs everything on its own.
Import from a provider, Replit docsYou rarely type git commands
The Git pane’s buttons cover staging, committing, and pushing. If you’d rather type them yourself, the workspace Shell stays in sync with whatever the pane just did.
Going live without external hosting
Skip third-party hosting and external database setup. Your database, backend, and domain live in the same Replit workspace. Just click Publish to go live.
| Host | Best for | Notes | Free tier |
|---|---|---|---|
| Autoscale | The dashboard itself | Grows with traffic and shrinks back when nobody is looking, which fits a marketing dashboard closely: it gets opened hard on a Monday morning and on the day a report is due, and barely touched in between. The default choice for the pages, and enough for everything a reader clicks. | Metered - billed with your plan |
| Scheduled | Importing on a timer | Runs on a clock rather than answering requests, which is the right shape for the job you will get tired of doing by hand: picking up the week’s export and loading it. Publish it separately from the app, make it safe to run twice on the same file, and give it a secret from Secrets rather than a session. Without it, the dashboard is exactly as current as the last time somebody remembered to upload something. | Metered - billed with your plan |
| Reserved VM | Imports that take real time | Dedicated compute that never sleeps. Worth comparing against Autoscale once your exports are large enough that parsing one is minutes rather than seconds, since a long job is what an autoscaling deployment handles least gracefully. | By machine size - billed with your plan |
| Static | Not this app | Files only, with no server behind them. Listed to rule out: your own server is what declares who is asking before any policy can act, the import parses uploads server-side, and the widget queries are answered per person, none of which exists here. | Metered - billed with your plan |
All of these are Replit rather than a third party, so the choice is shape rather than vendor, and this build wants two of them: one to serve the pages, and Scheduled for the import once you would rather not do it by hand. Review who the app is visible to before the first release, since Publishing is what sets it, and on this application that setting decides who can read what you spend.
One thing to check on the day you publish, and it is not the hosting. Nothing appears until a file has been imported, so a fresh deploy correctly shows zeros, and the first hour of production usually goes on establishing that the deploy is fine and nobody has uploaded anything yet.
Your database is already part of the workspace
Nothing to connect. Campaign rows are small and narrow and will not trouble the included allowance for years. It is the uploaded files that accumulate, and those belong in object storage rather than in a table.
| Service | Best for | Notes | Free tier |
|---|---|---|---|
| Replit Postgres | Data, built in | Managed Postgres with 20GB included free, provisioned from the same chat that builds the app, and since it is ordinary Postgres, the migrations from step 02 and the access policies from step 05 work exactly as they would anywhere else. What it does not include is a hosted identity service, which is why your own server is what declares the account and the organisation before any policy can act on it. | Free to start · 20GB included |
What to let a model do here, and what to compute first
A marketing dashboard is where the temptation to let a model narrate is strongest, because plain-English summaries of numbers are genuinely valuable and completely trivial to fake. One rule keeps all five of these honest: compute the figure with a query, then let the model choose the words.
Say only what it can actually know
Start here, before adding anything. A summary panel handed this period’s totals and nothing else cannot tell you they rose, and cannot project next week, but asked for insight it will do both, fluently and with a number attached. Better instructions will not help here. Compute last period with a query, pass those figures in, and the model finally has something real to describe. Anything it was not given, it may not mention.
Rewrite the written summary so it can only describe numbers it was given. First compute, with queries: this period’s totals for each metric, the same totals for the previous period of equal length, the change in each as an absolute and a percentage, and the same per channel and per campaign. Send only those computed figures to my ai function, and instruct it to describe what it is looking at without inventing any comparison, trend, projection or cause that is not in the numbers I sent, and to say when a change is too small to be worth a sentence. Print the figures it was given underneath the summary, name the two periods being compared, and if a metric has no previous period to compare against, have it say so rather than skip it.
Explain a movement after you have found its cause
The question a marketer actually asks is not what changed but why. That is a query (which campaigns and channels account for the difference between two periods, in order of contribution) and once you have the answer, a sentence explaining it is exactly the right job for a model.
Add a "why did this move" action on any metric card. Compute the answer first with queries: the campaigns and channels contributing most to the change between the two periods, with each one’s absolute contribution and its share of the total movement, and whether the movement came mostly from cost, from volume or from conversion rate. Then send only those computed contributions to my ai function for two or three sentences in plain language. Show the contributing rows underneath, and where a single campaign accounts for most of the movement, have it say that plainly rather than listing three things as if they were comparable.
Flag the rows that look wrong before they reach a chart
The dangerous import is the one that succeeds. A fortnight uploaded twice, a cost column in the wrong currency, a conversion count with an extra digit: all load cleanly and all move a card. Statistics can find the outliers. A model is useful for saying which of them look like mistakes rather than a good week.
After every import, run a check for rows worth a second look, computed with queries: amounts far outside the usual range for that campaign, cost-per-acquisition or return-on-spend figures orders of magnitude away from that channel’s norm, dates outside the period I said I was importing, days that already had data before this upload, and any metric that moved more than a threshold I set. Then send the flagged rows to my ai function for one line each on why it might be a mistake, and show me the list with an accept or investigate action per row. Nothing is edited or deleted automatically, and the flagged count sits beside the import summary where the reject count already is.
Ask your own numbers a question in words
A board can only answer the questions it was built to answer. The ones that turn up later (did the sale week pay for itself, which channel quietly got more expensive since spring) each arrive once and do not deserve a widget of their own.
Add a panel where I type a question about my own campaign data in ordinary words. My ai function turns it into a read-only query, runs that query under my own access rules so it can never reach another organisation’s rows, and uses the model only to word the answer. Under every answer, show me the query that ran, the rows it returned, and the period and filters that were in effect. Two hard rules: when a question needs data I do not import, it says so rather than estimating, and it never answers from what it already knows instead of from a query. It writes nothing, ever.
Put a ceiling on what it can spend
Requiring somebody to be signed in stops strangers spending your model budget and does nothing about the person who discovers the regenerate button. Every feature above is a request somebody can repeat, so the cap belongs in the same place as the key.
Add a spending ceiling to my ai function before I ship any of the features above. Record every call with the account that made it, the feature it came from, and the tokens it used. Then enforce two limits I can set without redeploying: how many calls one account may make in an hour, and a total for the whole project per month, refusing politely and telling the person when either is reached, rather than failing silently or charging on. Show me current usage against both limits on an admin screen, and log a refusal the same way as a call so I can see when a limit is doing something.
A cheap model is plenty for most of the prompts above. Save Pro’s stronger models for the one or two spots where the extra reasoning actually pays for itself. Add any provider key through Replit’s own Secrets tool rather than hard-coding it, and keep every AI feature behind one server-side function so a single key covers the whole app.
Get a head start with our template
Every route above begins with an empty folder. There is one that does not. This dashboard is built, runs, and already knows what to do with a messy export, so the weeks that a parser, a widget library, a query layer and two kinds of permission would have cost you turn into one upload and a look at the reject list.
Marketing Analytics Dashboard
The exact marketing dashboard this guide builds, packaged so you can open it, point it at your own backend, and make it yours from there. A marketing analytics tool where you bring in your data, assemble dashboards from a library of widgets and charts, and let AI point out what stands out. It turns scattered campaign numbers into a clear picture your whole team can read.
The key benefits of starting with a template
The import validates every row and keeps what it refuses, the six rates are computed with guards, dashboards are composed from a widget library, and each widget’s query runs under the permissions of whoever is looking. That is most of the saving. The sharing, the invitations and the notifications are done too.
Building the core from scratch
~103 hrs
Opening the template, already built
~1 hr
~102 hrs of building you skip
Two different measurements on purpose. The larger figure is what it costs to build this application. The ~1 hr is what it costs to adopt the finished one: connect your own database, put one real export through it, and settle who may build a board against who may only open one. Longer than most templates in this catalogue take, because this is one of the few that shows you nothing until real data has been through it. What neither figure includes is the argument about what a conversion means in your business, since that costs the same whichever route you take.
An import that already copes, and keeps its refusals
Upload a CSV and a server-side function parses it, checks that the required columns are present, validates every row (dates parseable, numbers numeric and not negative where that makes no sense), writes what passes into the campaign tables and every failure into a rejects table with its line number, its raw data and the reason. Re-uploading the same fortnight is a decision rather than an accident: rows are keyed on date, channel and campaign, and you choose per import whether a repeat is skipped or overwritten.
Two data shapes, both wired up
A wide table of one row per day, channel and campaign carrying impressions, clicks, conversions, cost and revenue, and a canonical star schema alongside it: a campaign dimension with separate fact tables for sessions, cost and conversions. Two import functions, one per shape. The wide table is the quick path. The star schema absorbs a new metric without touching every screen. Having both makes the choice in decision 03 above a preference rather than a rebuild.
Six widgets, and the editor that composes them
A library of line, area, bar and pie charts, a metric card and a data table, each one configurable, arranged into saved layouts by anybody with the editor role. Behind them a function answers each widget’s own query, building its database client from the viewer’s own credentials rather than a service key, so every access rule below applies to every widget automatically, including one added months later. Four filters cross the whole board at once, remembered between visits: date range, channel, campaign and geography.
83 live access policies, over two permission axes
Three roles from a real database enum (viewer, editor and administrator) kept in their own table behind checks running with the database’s authority rather than the caller’s, and 83 live row-level security policies over 24 tables. Then the second axis, which is what makes this usable with clients: a grant on a single dashboard, attachable to an invitation so it transfers when the account is created. Somebody can be given one board without the run of the place. How far the roles reach is worth knowing before you go live: read the access section above, because on a deployment holding more than one client’s numbers there are policies you will want to narrow.
The written summary, and the honest note about it
A server-side function sends the current metrics to OpenAI and returns a short plain-English summary for the top of the board. It requires a signed-in user before it spends anything, tells a rate limit apart from an out-of-credit error, retries a provider outage three times with a growing pause, and takes its model from an environment variable so swapping it is configuration rather than a code change. What it is given is the current totals and the filters in effect (no history) so treat any comparison or projection in its text as a turn of phrase rather than a finding. The first AI prompt above is the version that computes the comparison before mentioning it.
A codebase your AI tool can keep extending
Types on everything, one hook per thing that gets queried, folders named after features rather than file extensions, and comments where the reasoning would not survive a cold read. The widget layer is where that pays off: a seventh kind of widget is the change you are most likely to want, and the six that exist show where a new one plugs in.
From founders who build on our templates
As an agency, clean handoffs are everything. This template is so well-architected that we can restyle the interface, connect the client’s data, and hand over a finished product, without wasting billable hours untangling someone else’s mess.
Matt GrahamFounder & CEO, RapidDevCommon questions
You upload files, and that is the first thing to know before comparing it with anything on the market. There is no authenticated connector to an ad platform: you export a CSV from each one, upload it, and a server-side function validates and loads every row. That is the honest trade. The products that do connect are the ones metered per client or per data feed, and maintaining connectors against platforms that rename fields is most of what that fee pays for. A connector to one specific platform is a well-defined addition, and it is the thing most people want second, after a month of their own numbers has actually landed.
Every row that fails is written to a rejects table with its line number, the original row and what was wrong with it, and you are told how many failed as soon as the import finishes. Why that matters: a board silently short of two hundred rows still draws a confident chart, and nothing about it looks wrong. So read the reject list after every upload as a habit. In practice most entries turn out to be one date format or one renamed column, which is a five-minute fix the moment you can see it. One thing to know as it ships: the rejected rows are kept and counted, but the download link offered for them does not work, because the file is written to a private bucket and linked as though it were public. The rows are in the database. Fixing the link is a signed URL.
Trust the numbers it quotes and not the story it tells about them. As it ships, the summary is sent the current totals and the filters in effect, and nothing else, no previous period and no history. The metrics it repeats are therefore real. Any comparison, trend or projection in the same sentence is the model being fluent, because it has no data to derive one from and the example in its instructions encourages it to try. That is a genuine limitation, stated here rather than buried: treat the paragraph as a readable caption, and use the first AI prompt on this page to replace it with a version that computes the comparison first and may only describe what it was given.
No, and a table in the schema makes it look as though it might. The exchange-rate table has access rules on it and nothing in the application reads it, so every figure you see is in whatever currency your imported rows were in. For most teams advertising in one currency that is a non-issue. If you spend in three, the honest description is that this is an addition rather than a setting: convert on import or at query time, store which rate you used, and mark any total that mixes them. That last part is what stops an awkward conversation about a number being slightly off.
Yes, and it is one of the two things this template was shaped around. Access is the union of a role and a set of grants: a viewer can be given one specific dashboard, by email, before they have an account at all. The grant rides on the invitation and transfers when they accept. Before you point a client at it, read the access section above and narrow what you find there: some rules as shipped grant an editor or an administrator reach across dashboards without checking which organisation the dashboard belongs to, which is harmless on a single-company deployment and not what you want when two clients share one database.
Very little, and it is the one running cost that is genuinely usage-based. The summary sends a few hundred tokens and gets a short paragraph back. At the published price of the small model it ships with, that is a fraction of a cent per refresh, so a thousand refreshes in a month costs cents rather than dollars. Two caveats worth more than the arithmetic: nothing in the template caps that spend, so a per-account and per-month ceiling is the sixth AI prompt above and worth adding before you ship, and the model is chosen by an environment variable, so pointing it at something larger changes the sum entirely.
No. You describe what you want in the chat, and the Agent handles the rest: the code, the database, and a live version of the app right there in the workspace. The setup section above covers the account and plan you need first.
Replit hosts it. Publishing takes the same project live on a Replit domain, or your own if you connect one, with monitoring and access controls included, so you never open a separate hosting account.
Starter’s daily credits and a paid plan’s monthly grant both refill on their own schedule. Hitting either limit mid-build doesn’t touch what you’ve already made. Move up a plan for more headroom right away, or wait it out.
References
Sources checked August 2026- 01Highlights and Insights Report, 35th edition (marketing technology performance, n=308). cmosurvey.org (fielded January 2026)
- 02Pricing (per-client plan, unlimited users, add-ons), AgencyAnalytics. agencyanalytics.com
- 03Pricing (dashboard tiers, unlimited users), DashThis. dashthis.com
- 04Pricing (data sources, destinations and users; both billing bases), Supermetrics. supermetrics.com
- 05Pricing (two “starts from” floors, connector counts), Funnel. funnel.io
- 06Pro subscription overview (per-user licences, one Cloud project), Data Studio - formerly Looker Studio. docs.cloud.google.com
- 07Pricing (Pro plan, free-tier limits, project pausing), Supabase. supabase.com
- 08API pricing (per-million-token rates for the small models). developers.openai.com
- 09Web developer hourly rates 2026 (freelance and agency benchmarks). developex.com
- 10Pricing (Starter, Core, Pro), Replit. replit.com
- 11Built-in database, Replit docs. docs.replit.com
- 12Publishing overview, Replit docs. docs.replit.com
- 13Deployment types, Replit docs. docs.replit.com
- 14Checkpoints and rollbacks, Replit docs. docs.replit.com
- 15Using the Git pane, Replit docs. docs.replit.com
- 16Import from a provider, Replit docs. docs.replit.com
- 17Secrets, Replit docs. docs.replit.com
This guide is general information, not advice about how to measure your marketing. What counts as a conversion, and which platform’s figure is authoritative when two disagree, are decisions for your own business, and a number on a dashboard is only as good as the definition underneath it. Third-party prices, plan terms, and market rates are quoted from the sources above and were last checked on the date shown. Vendors change them without notice, so confirm before you budget. Build hours and the cost estimates derived from them are our own estimates, not quotes. Replit is a product of Replit, Inc. Verify current capabilities and pricing before relying on them.