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 requiredNot sure which file holds a piece of text? The page maps label every section of every page with its file.
Your details first
Section titled “Your details first”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.
Page copy
Section titled “Page copy”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.
Collections
Section titled “Collections”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.
Adding a service
Section titled “Adding a service”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:
slugis the URL:/services/rail-freight.summaryappears in the service lists (Home, Services), next to the title on its page, and is its search description.imageis the wide photo at the top of its page, also used in the lists and shared with its link (about 2400 × 1600).aboutis the block under the photo: a title, a text and fourfacts(a value and a label each).benefitsis “Our solutions”: a title and four items. Their titles are also the “Key Benefits” on the Services page.lanesis “Rail Freight Lanes”: a title, a text anditems, 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.
Adding a trade lane
Section titled “Adding a trade lane”{ "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
sluginlanes.items, and the first lane of the file is the “New Lane” card in the Home hero. transitis the chip on the lane card,frequencythe small line above its title, andaveragethe chip in the Home hero.descriptionis kept with the lane but not shown on the pages.imageis 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).
Removing an item
Section titled “Removing an item”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.
What each field means
Section titled “What each field means”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.
Rich text: posts and legal pages
Section titled “Rich text: posts and legal pages”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.
Images
Section titled “Images”- Put the file in
src/assets/images/. - 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.
Text that is not in content/
Section titled “Text that is not in content/”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) inNavigation.astroandsrc/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) insrc/layouts/Layout.astro.
Search for the word and you will find it.
After editing
Section titled “After editing”npm run buildA successful build means every page, link and image still resolves. See Going live for the rest of the checklist.