Skip to content
Sign inJoin waitlist

Pages and sections — Kalme Astro

A page is a small file that picks sections and hands them content. src/pages/services.astro is the whole Services page:

---
import ServicesListSection from "../components/sections/ServicesListSection.astro";
import servicesPage from "../content/pages/services.json";
import Layout from "../layouts/Layout.astro";
import { getServices } from "../lib/content";
import { toServiceRow } from "../lib/to-ui";
const services = await getServices();
---
<Layout title={servicesPage.meta.title} description={servicesPage.meta.description}>
<ServicesListSection {...servicesPage} services={services.map(toServiceRow)} />
</Layout>
  • Layout gives the page its browser title, description, canonical link, share image, header and footer.
  • getServices() reads the collection (see Editing the content); toServiceRow turns each service into what the row component shows (src/lib/to-ui.ts).
  • The section renders them.

In src/components/sections/. Each takes its copy from a page file and its items from a collection.

Section Used on
HomeHeroSection, PageVideoSection, HomeAboutSection, HomeServicesSection, HomeProjectsSection, TestimonialsSection, HomeBlogSection, CtaSection Home
AboutHeroSection, ValuesSection, PageVideoSection, TeamSection About
ServicesListSection Services
ServiceHeroSection (with ServiceContentSection inside it), MoreServicesSection Service
ProjectsListSection Projects
ProjectHeroSection, ProjectStorySection (three times: overview, process, result), RelatedProjectsSection, CtaSection Project
BlogListSection Blog
PostMainSection, MorePostsSection Blog post
ContactSection Contact
LegalSection Terms of Service, Privacy Policy
NotFoundSection 404

PageVideoSection and CtaSection appear on two pages each: the video comes from the page file (video in home.json and about.json), the call to action from cta in src/content/site.json, so changing it once changes it everywhere.

Every page type with each section outlined and labelled: the component that renders it, the file to edit, and where its copy (src/content/pages/*.json) and items (src/content/collections/*.json) come from. Header and footer take their links and details from src/content/site.json. Photos are the placeholders of the download.

Home: sections and the files behind them

About: sections and the files behind them

Services: sections and the files behind them

The service’s title, description and first image open the page; “What We Offer”, “Key Benefits”, “Tools” and the pricing card come from the same service in services.json, their headings from src/content/pages/service.json.

Service: sections and the files behind them

Projects: sections and the files behind them

Project: sections and the files behind them

Blog: sections and the files behind them

Blog post: sections and the files behind them

Contact: sections and the files behind them

Legal: sections and the files behind them

Page not found: sections and the files behind them

Move the lines in the page file, or delete one. To take the testimonials off the home page, delete <TestimonialsSection … /> from src/pages/index.astro (and its import line); its copy in src/content/pages/home.json can stay or go.

  1. Add the URL to src/content/routes.ts:
    pricing: "/pricing",
  2. Add its copy to src/content/pages/pricing.json, with a meta (title, description):
    { "meta": { "title": "Pricing", "description": "What our services cost, from a single campaign to a monthly retainer." } }
  3. Create src/pages/pricing.astro:
    ---
    import CtaSection from "../components/sections/CtaSection.astro";
    import pricingPage from "../content/pages/pricing.json";
    import siteContent from "../content/site.json";
    import Layout from "../layouts/Layout.astro";
    ---
    <Layout title={pricingPage.meta.title} description={pricingPage.meta.description}>
    <CtaSection {...siteContent.cta} />
    </Layout>
  4. Link to it: add { "label": "Pricing", "href": "/pricing" } to navigation in src/content/site.json, or to the footer’s quickLinks.
  5. Add it to src/pages/sitemap.xml.ts, next to the other top-level pages.

Delete its file under src/pages/, its entry in navigation, in the footer’s links and in src/pages/sitemap.xml.ts, and the route in routes.ts. npm run check then tells you about anything still pointing at it through routes.

src/pages/blog/[slug].astro builds one page per post:

export const getStaticPaths = (async () => {
const posts = await getPosts();
return posts.map((post) => ({ params: { slug: post.slug }, props: { post, posts } }));
}) satisfies GetStaticPaths;

Add a post to src/content/collections/posts.json and its page, its links and its sitemap entry appear on the next build; any other slug is a 404. The same pattern builds the service, project and legal pages.

src/pages/404.astro, with its copy in src/content/pages/not-found.json. It is noindex, like the style guide. Static hosts serve 404.html for any unknown address.

  • src/pages/sitemap.xml.ts builds sitemap.xml from routes.ts and the collections, so new items list themselves. The 404 page and the style guide are left out.
  • src/pages/robots.txt.ts builds robots.txt and points at the sitemap. Both use site.url, so set your domain before publishing.
  • Every indexable page gets a title, a description, a canonical link, an og:url and a share image from src/layouts/Layout.astro. A title that would be longer than 60 characters with | Kalme appended is shown without the suffix.
  • noindex pages: the style guide and the 404 page.