Skip to content
Sign inJoin waitlist

Editing the content — Ciketo Astro

All the words of the site live in src/content/, and its photos in src/assets/images/. You edit plain JSON files (text in quotes, commas between entries), and the site updates while npm run dev is running. No CMS is needed. If you would rather manage content in Sanity, Payload, Strapi or Directus, see Using a headless CMS.

src/content/
site.json name, description, domain, menu, contact details, footer, and the parts several pages share
pages/home.json the copy of the home page
pages/about.json …and of every other page
collections/*.json services, trade lanes, posts, testimonials, clients, legal pages
schemas.ts the fields each kind of item has, and which are required

Not sure which file holds a piece of text? The page maps label every section of every page with its file.

src/content/site.json holds what appears on every page, and the parts that several pages share:

Field Shown where
site.name Logo label for screen readers, the end of the pages’ browser titles, share cards
site.title The browser title of the home page, used as it is. Other pages use their own title and add site.name after it.
site.description Search results and share cards for the home page, and for any page without its own description
site.url Set this to your domain before publishing. Canonical links, share links, sitemap.xml and robots.txt are built from it.
navigation The menu in the header (and the phone menu)
headerAction The “Get a Quote” button on the right of the header
clock.locale The language of the date in the header clock: "en-US" writes “October 3, 2026”, "en-GB" “3 October 2026”. The clock shows each visitor’s own time.
contact Address, phone and e-mail (footer and Contact), the support e-mail (the help card on Home), and the “Phone:” and “Email:” labels
socials The Social column of the footer
footer The tagline, the Company and Services columns, the column titles, the copyright line ({year} becomes the current year) and the legal links
clientsIntro “Since 2011” and the line beside the client logos (Home, About)
facts The four figures with their text: “Why Choose Us” on Home, “Who we are” on About
howItWorks “How It Works” and its four steps, each with a title, a text, a detail and a photo (Home, Services, every service page)
industries “Industries We Serve”: six cards (items) and the five photos between them (images) (Home, About)
quoteForm The quote form (Home, Contact, every service page): label, title, text, photo, fields, button and messages (Forms)
cta The call to action above the footer on Home, About, Services and Blog: label, title, text, photo and two buttons

site.url is read by astro.config.mjs at build time, so restart npm run dev after changing it.

The demo’s e-mails (hello@example.com, support@example.com), phone number and address are placeholders: replace them all. Two things repeat them by hand:

  • “Call Customer Service” in the call to action calls cta.secondary.href ("tel:+18005550147"): change the number there too.
  • The footer’s Services column lists three services and “All Services” (footer.services.links): update it when you rename, add or remove a service.

The industries alternate cards and photos: a photo follows cards 1, 2, 3, 5 and 6, and the first photo is wide on tablets and desktops. Keep six cards and five photos, or change the pattern (photoAfter) in src/components/sections/IndustriesSection.astro.

There is one file per page in src/content/pages/. Each holds the sections of that page and, for the About, Services, Blog and Contact pages, a meta entry (the 404 page has a meta.title only):

{
"meta": {
"title": "About Us",
"description": "A logistics partner that moves cargo across cities, countries and continents: who we are, a message from our leader and the industries we serve."
},
"hero": { "…": "…" }
}

title becomes the browser tab (“About Us | Ciketo”), and description the text in search results and share cards (80–160 characters). Change what is inside the quotes and keep the field names: the sections read them by name. JSON has no comments and no trailing commas; if the site stops building, that is usually why.

File Page
home.json Home: the hero (its figures and the “New Lane” label), “Why Choose Us” with a quote, the services intro and its help card, the photo band, the testimonials label, the blog intro and the FAQ (faq.items). It has no meta: see site.title and site.description above.
about.json About: the hero, “Who we are” and the leader’s message (leader)
services.json, blog.json The pages of the same name
contact.json Contact: the title, text, photo and the business hours (hours.rows)
service.json The words shared by every service page (“Services”, “Our solutions”, “Lanes”, “Departure:”, “Arrival:”) and the photo beside the key benefits (benefitsImage)
post.json The words shared by every post (“Date:”, “Other Posts”)
legal.json “Last Updated:” on the legal pages
not-found.json The 404 page: its browser title (meta.title), the big “404”, the text, the button and the photo

The four client names in the Home hero come from clients.json, and its lane card from lanes.json.

src/content/collections/ holds the lists the site builds pages from:

File What it feeds
services.json The Services page, one page per service, and the services list on Home (all of them, numbered 01, 02, … in order)
lanes.json The trade lanes on each service page (the ones the service lists) and the “New Lane” card in the Home hero (the first lane)
posts.json The Blog (six at a time as you scroll), one page per post, Home (the first three) and “Other Posts” at the end of a post (up to three of the same category)
testimonials.json The testimonials on Home (all of them)
clients.json The logos moving across Home and About (all of them) and the names in the Home hero (the first four)
legal.json One page per legal text: /legal/privacy-policy, /legal/terms-conditions, /legal/cookie-policy

The order of the items in the file is the order on the site.

Copy an existing entry, paste it after the last one (mind the comma between entries), and change the values:

{
"slug": "rail-freight",
"title": "Rail Freight",
"summary": "Move containers and bulk cargo overland between ports and inland hubs…",
"image": "/images/service-rail-freight.jpg",
"about": {
"title": "…",
"text": "…",
"facts": [
{ "value": "40+", "label": "Rail Terminals" },
{ "value": "…", "label": "…" },
{ "value": "…", "label": "…" },
{ "value": "…", "label": "…" }
]
},
"benefits": {
"title": "…",
"items": [
{ "title": "Block Trains", "description": "…" },
{ "title": "…", "description": "…" },
{ "title": "…", "description": "…" },
{ "title": "…", "description": "…" }
]
},
"lanes": {
"title": "…",
"text": "…",
"items": ["ningbo-hamburg", "jebel-ali-rotterdam", "singapore-rotterdam"]
}
}

Here’s what each field does:

  • slug is the URL: /services/rail-freight.
  • summary appears in the service lists (Home, Services), next to the title on its page, and is its search description.
  • image is the wide photo at the top of its page, also used in the lists and shared with its link (about 2400 × 1600).
  • about is the block under the photo: a title, a text and four facts (a value and a label each).
  • benefits is “Our solutions”: a title and four items. Their titles are also the “Key Benefits” on the Services page.
  • lanes is “Rail Freight Lanes”: a title, a text and items, the slugs of the lanes to show, in that order.

The footer’s Services column and the quote form’s shipping methods are separate lists in site.json (footer.services.links, quoteForm.fields.method.options): add the service there too if you want it listed.

{
"slug": "rotterdam-new-york",
"title": "Rotterdam → New York",
"departure": "Port of Rotterdam, Netherlands",
"arrival": "Port of New York and New Jersey, USA",
"transit": "9–12 Days",
"average": "10 Days",
"frequency": "Weekly",
"description": "…",
"image": "/images/lane-rotterdam-new-york.jpg"
}
  • A lane has no page of its own. It shows on every service page that lists its slug in lanes.items, and the first lane of the file is the “New Lane” card in the Home hero.
  • transit is the chip on the lane card, frequency the small line above its title, and average the chip in the Home hero.
  • description is kept with the lane but not shown on the pages.
  • image is about 800 px wide.

Adding a post, testimonial, client or legal page

Section titled “Adding a post, testimonial, client or legal page”
Add Fields
Post slug (the URL, /blog/<slug>), title, excerpt (the card text, the text beside the title on its page and its search description), image (about 2400 × 1600), category (any words: the label above the title; posts with the same one are listed under each other as “Other Posts”), date ("2026-08-26", shown as “Aug 26, 2026”), body (rich text)
Testimonial slug, title (the quote’s headline), quote, name, role, company, photo (a square portrait, about 800 × 800), rating (out of 5: 5 shows “5.0/5” and five stars)
Client slug, name (the logo’s description, and the list in the Home hero), logo (an SVG or PNG in src/assets/images/, shown 16 px tall)
Legal page slug (the URL, /legal/<slug>), title, updated ("2026-09-02", shown as “Last Updated: Sep 2, 2026”), body (rich text). Its search description is its first paragraph. Link it from the footer in footer.legal.

Home shows every testimonial: the chosen client’s portrait is large and the others share the space that is left, so keep to a handful (the demo has four).

Delete the entry (and the comma before it, if it was the last one). Its page, its card and its sitemap entry disappear on the next build. A service names its lanes by slug: if you delete a lane, take its slug out of lanes.items too (a slug that matches no lane is skipped). Take a deleted service out of footer.services.links, and a deleted legal page out of footer.legal.

src/content/schemas.ts is the reference: it lists every field, which are optional (.optional()), and a comment for the ones that are not obvious. npm run check reports a missing or misspelled field with the collection, the item and the field.

A post body, or the text of a legal page, is a list of blocks:

[
{ "type": "heading", "text": "Why transit times vary" },
{ "type": "heading", "level": 3, "text": "A smaller heading" },
{ "type": "paragraph", "text": "A plain paragraph." },
{ "type": "paragraph", "highlight": "Bold start:", "text": " and the rest of the paragraph." },
{ "type": "list", "items": [
[{ "type": "paragraph", "text": "First item" }],
[{ "type": "paragraph", "highlight": "Second:", "text": " with a bold start" }]
] }
]

A heading is a Heading 2 unless you add "level": 3. Each list item is itself a list of blocks, so an item can hold paragraphs and a nested list; add "ordered": true for numbers. Text is plain: the bold part is highlight, not **stars**, and links are not part of the blocks.

  1. Put the file in src/assets/images/.
  2. Refer to it in the content as /images/<file>.

Astro resizes, compresses and converts the photos (to WebP) when the site builds, so each device gets a size that fits. Use photos at least as large as the demo’s, which the placeholders show:

Photos Size
Full width: the Home hero (also behind the 404 page), About, the photo band, the call to action, services, posts about 2400 × 1600
The Contact cover (tall, behind the form) 2400 × 3600
“Who we are” on About (also beside the key benefits on every service page) 1200 × 1600
The first “Why Choose Us” photo (also beside the quote form), the first industry photo 1200 px wide
The steps, the lanes, the help card on Home 800 px wide
The second “Why Choose Us” photo, the other industry photos 600 px wide
Testimonial portraits 800 × 800
James Carter (About, Home) and Sarah Mitchell (Home) 600 × 600 and 200 × 200

Keep the demo’s aspect ratios: the layout crops to fit. A content path that points to a missing file stops the build with its name. The client logos (client-*.svg) are used as they are, 16 px tall at their own width.

Files that browsers and social networks ask for by name live in public/:

File What it is
public/icon.svg, public/apple-icon.png The icon in the browser tab and on a phone’s home screen (the PNG is 180 × 180)
public/opengraph-image.png The picture shown when someone shares a link (2400 × 1260; 1200 × 630 or larger)

Its description for screen readers is set in src/layouts/Layout.astro. Every page uses this image when shared, except the service and post pages, which share their own photo.

Almost every word is content, including the form’s messages (quoteForm in site.json). The few interface words that never change per site live in the code:

  • “Menu” (the phone menu button’s label for screen readers) in src/design-system/components/navigation/Hamburger.astro, and “Main” and “Phone” (the names of the two menus) in Navigation.astro and src/components/site/Header.astro;
  • “Clients” (the name of the logo row for screen readers) in src/components/sections/ClientsSection.astro;
  • “/5” after a testimonial’s rating in src/components/sections/TestimonialsSection.astro;
  • the description of the share image (“logistics and shipping website template”, after site.name) in src/layouts/Layout.astro.

Search for the word and you will find it.

Terminal window
npm run build

A successful build means every page, link and image still resolves. See Going live for the rest of the checklist.