Skip to content

Plan & Pricing Data

The pricing page (/pricing/) is different from the homepage. Instead of one JSON file, it uses two:

  1. src/data/pricing/data.json — the content of the page (titles, descriptions, hero images, CTA buttons).
  2. src/data/pricing/pricing.json — the price list (the actual pricing tiers).

This separation is intentional. The page has two concerns:

  • Content — the story, the hero, the call-to-action.
  • Price list — a repeatable list of tiers that could change independently.

By splitting them, you can update prices without touching the page copy, and vice versa.


src/data/
├── plan/
│ └── data.json ← page content
└── pricing/
└── pricing.json ← price tiers

Both files are imported by the same MDX page: src/content/docs/plan.mdx.


This file describes the page as a whole: its meta info, hero images, and two big CTA blocks.

KeyTypePurpose
urlstringCanonical URL for this page
titlestringPage title (browser tab + SEO)
descriptionstringMeta description + hero subtitle
introstringThe main hero headline
iconstringFavicon-style icon
imagestringSmall hero illustration
hero_imagestringLarge hero illustration
themesstringTitle of the first CTA block
themes_textstringDescription of the first CTA block
themes_buttonstringButton label for the first CTA
themes_linkstringButton URL for the first CTA
hirestringTitle of the second CTA block
hire_textstringDescription of the second CTA block
hire_buttonstringButton label for the second CTA
hire_imagestringIllustration for the second CTA
hire_linkstringButton URL for the second CTA

Notice the pattern: one section = title + text + button + link + image. This repeats for both the “Buy Template” block and the “Hire” block.

"themes": "Buy Template - $65",
"themes_text": "Complete Astro Docus source code...",
"themes_button": "Buy Now - $65",
"themes_link": "https://creativitaz.gumroad.com/l/starlightastrodocus"
"hire": "Need Custom Setup?",
"hire_text": "Hire for installation, CMS integration...",
"hire_button": "Hire on Fiverr",
"hire_image": "/src/assets/webdeveloperastrojs.svg",
"hire_link": "https://www.fiverr.com/creativitas/design-modern-documentation-website-astro-js-stalight"

Changing the button text or link is a matter of editing one string in data.json.


This file is much simpler. It contains only a single array — resources — that lists every pricing tier.

{
"resources": [
{
"title": "Basic",
"text": "Basic package - Astro Docus template source code...",
"price": "$65",
"link": "https://creativitaz.gumroad.com/l/starlightastrodocus"
},
{
"title": "Standard",
"text": "Standard package - Template source code + Tina CMS...",
"price": "$105",
"link": "https://www.fiverr.com/creativitas/..."
}
]
}
FieldTypePurpose
titlestringTier name (Basic, Standard, Premium…)
textstringShort description of what is included
pricestringDisplay price (can be "$65" or "Custom")
linkstringWhere the buy button goes

Because the number of tiers may change. Add a new object, and a new pricing card appears. Remove one, and the card disappears. The page adapts automatically.

This is the same “array looping” concept used on the homepage — see Home Data for the full pattern.


The page src/content/docs/plan.mdx imports plan/data.json:

---
import Data from '../../data/pricing/data.json';
import { Picture } from 'astro:assets';
---

Then it renders the page in two halves:

The top of the page uses Data.image, Data.intro, Data.description:

<div class="col-md-10 mx-auto text-center p-3">
<Picture src={Data.image} alt={Data.description} ... />
<h2 class="small">{Data.description}</h2>
<h3 class="fw-bold mt-3 mb-3">{Data.intro}</h3>
</div>

Then two CTA blocks — “Buy Template” and “Hire”:

<div class="col-md-5 p-3 p-md-5">
<h3><strong><a href={Astro.url}>{Data.themes}</a></strong></h3>
<p>{Data.themes_text}</p>
<p><a href={Data.themes_link} class="btn btn-primary rounder">{Data.themes_button}</a></p>
</div>
<div class="col-md-5 p-3 p-md-5">
<h3><strong><a href={Astro.url}>{Data.hire}</a></strong></h3>
<p>{Data.hire_text}</p>
<p><a href={Data.hire_link} class="btn btn-primary rounder">{Data.hire_button}</a></p>
</div>

Half 2 — Price list from pricing/pricing.json

Section titled “Half 2 — Price list from pricing/pricing.json”

The price list itself is rendered by a widget that reads pricing/pricing.json. The MDX page simply imports and places it:

import Pricing from '../../widget/Pricing.astro';
<Pricing />

The Pricing.astro widget then loops through resources:

---
import Pricing from '../../data/pricing/pricing.json';
---
<div class="row">
{Pricing.resources.map(({ title, text, price, link }) => (
<div class="col-md-3 p-3">
<div class="card h-100">
<div class="card-body">
<h3>{title}</h3>
<p>{text}</p>
<h4 class="fw-bold">{price}</h4>
<a href={link} class="btn btn-primary">Buy Now</a>
</div>
</div>
</div>
))}
</div>

Each object in resources becomes one pricing card.


src/data/pricing/data.json src/data/pricing/pricing.json
│ │
▼ ▼
src/content/docs/plan.mdx src/widget/Pricing.astro
(imports data.json directly) (imports pricing.json)
│ │
└──────────────┬─────────────────────┘
▼
/pricing/ page

Two files, one page. The MDX handles layout and copy; the widget handles the repeating tier list.


  1. Open src/data/pricing/data.json.
  2. Find "intro".
  3. Change the value, save.
Section titled “Scenario 2 — Update the Fiverr hire link”
  1. Open src/data/pricing/data.json.
  2. Find "hire_link".
  3. Replace the URL, save.
  1. Open src/data/pricing/pricing.json.
  2. Add a new object to the resources array:
{
"title": "Enterprise",
"text": "Full custom development with SLA support.",
"price": "$500+",
"link": "https://example.com/contact"
}
  1. Save. A new card appears automatically.
  1. Cut the object you want to move.
  2. Paste it in a different position within the array.
  3. Save. The cards re-render in the new order.
  1. Delete the object from the array.
  2. Save.

You might wonder why the pricing tiers are not simply merged into plan/data.json. Two reasons:

  1. Separation of concerns. Page copy and product data are different things. The copy changes when you rebrand; the tiers change when you adjust your offer.
  2. Reusability. The same pricing.json can be used on the homepage, a pricing modal, or a blog post without duplicating content. The plan/data.json is specific to the /pricing/ page.

This is the same reason the homepage uses a single home.json — because the homepage has no reusable sub-components. The plan page does.


  1. Keep valid JSON. No trailing commas, no comments, double quotes only.
  2. Do not change key names unless you also update the widgets that read them.
  3. Keep tier shapes consistent — every object in resources should have title, text, price, link.
  4. Use /src/assets/... paths for images referenced in data.json.
  5. Prices are strings, not numbers. Use "$65", not 65. This allows "Custom" as a value.

plan/data.json → page copy, hero, CTA blocks
pricing/pricing.json → array of pricing tiers
│ │
└────────┬───────────────┘
▼
plan.mdx page
▼
/pricing/ URL
  • Two files, one page. Content and price list are separate.
  • plan/data.json is read directly inside plan.mdx.
  • pricing/pricing.json is read by a Pricing.astro widget and looped.
  • Add a tier by adding one object to resources.
  • Change copy by editing one string in data.json.

👉 Next: docs/data/index-page.md — how JSON powers documentation landing pages.