Collections (Services, Projects, Careers & More)
Collections
Skill level: Beginner · Files: src/content/<collection>/<language>/
A collection is a folder of similar entries: services, projects, job openings, blog posts. Each entry is one file and one page. The list page of the collection (for example /projects/) gets its own title and section blocks from the -index.md file in the same folder.
Add An Entry
- Copy an existing entry in the folder, for example
src/content/projects/english/project-1.mdx. - Rename the copy and change its
customSlug, which sets the URL (/projects/my-new-project/). Collections whose entries have nocustomSluguse the file name. - Change the frontmatter values and the Markdown body. Keep the keys.
- Add the same file in each other language folder with the translated text and the same
customSlug.
To hide an entry, set draft: true. It still shows in npm run dev, but it is left out of the built site, the lists, and the sitemap. Services, projects, and open roles are ordered by weight (lowest first); blog posts by date (newest first).
A new entry with only a title won’t break the site, and it builds. Its card or page shows what you filled in, and images and other fields stay empty until you add them, so fill them in before you publish.
Collection Reference
The fields every page accepts (title, description, image, draft, customSlug, SEO fields) work on every entry. The table lists what each collection uses on top.
| Collection | Folder | URL | Fields |
|---|---|---|---|
| Services | services/ | /services/<customSlug>/ | icon, image, weight, date, Markdown body, optional ctaSection |
| Projects | projects/ | /projects/<customSlug>/ | date, weight, categories, information, image, headerImageAlt, quote, Markdown body |
| Careers | careers/ | /careers/<file>/ | department, location, type, salary, date, validThrough, weight, responsibilities, requirements, benefits, button |
| Blog posts | blog/ | /blog/<customSlug>/ | date, updated, author, categories, tags, image |
| Authors | author/ | (no page) | title (the name), description, image |
| Team | team/ | /team/ only | One list in -index.md: title (the name), image, profession, description, email, phone, social |
| Testimonials | testimonial/ | (no page) | One list in -index.md: content, image, customer (name, role, system, rating) |
Services
The service pages power the services overview (/services/), the services section on the homepages, and the Services column in the footer. The Services mega menu in the header is separate: its links are in the menu file.
iconis a Lucide icon name in PascalCase, such as"BatteryCharging".imageis the photo on the card and at the top of the service page.weightsets the order. The homepages show the first 4, the overview the first 6 (options.limitin eachservicesSection), and the footer the first 6.- The body is Markdown: headings, lists, and a
<Button>at the end. The[.h4]and[.h5]after a heading set its size (see Writing Content). - The page header badge comes from
badgeSecondaryinservices/english/-index.md.
If you rename a service’s customSlug, update its links in the Services mega menu of every src/config/menu.*.json file.
Projects
Each project is a short case study:
title: "A family home that covers most of its own power"customSlug: "family-home-rooftop"description: "A 7.2 kWp roof array and a 10 kWh battery for a four-person household with an electric car."date: 2026-06-12weight: 1categories: - Residential # also the badge on the project pageinformation: # the facts list on the project page - label: "System size" value: "7.2 kWp" - label: "Location" value: "Riverside"image: "/images/photos/project-home-unsplash-VX0ZEZSwqnY.jpg"headerImageAlt: "Single-storey home with a full solar array on the roof"quote: # optional customer quote after the text enable: true content: "We used to dread the winter bills..." name: "Homeowner" role: "Riverside · 7.2 kWp + 10 kWh battery"In information, value: "date" shows the project’s date and value: "categories" its categories. The grid on /projects/ shows options.limit projects (set in projects/english/-index.md, false for all), ordered by weight.
Careers
One file per open role, for example src/content/careers/english/electrician.md:
departmentgroups roles on/careers/(setgroupByDepartment: falseincareers-list-section.mdfor one flat list);weightorders them.typeisfull-time,part-time, orcontract. The labels shown for each type are inlabels.typesofcareers/english/-index.md.location,type, andsalaryappear as chips under the title.salaryis free text, such as"$34 to $42 per hour".responsibilities,requirements, andbenefitsare lists, shown under the headings set inlabels.- The Apply card comes from
applyincareers/english/-index.md. Its button ships asmailto:[email protected]: point it at your email, form, or hiring system. A role can set its ownbuttonto override it. date,validThrough,location,type, andsalaryfeed the job posting data (JobPosting) search engines read. Google allows this data only for real, open positions, so replace or delete the sample roles before launch, and remove a role when it is filled.
When no roles are published, /careers/ shows emptyText from careers-list-section.md.
Blog
- Posts are
.mdxfiles withtitle,customSlug,metaDescription,image,date,author,categories,tags, anddraft. - Author.
author: "Clara Morel"is shown under the title. If a matching file exists insrc/content/author/english/(the name in lowercase, with dashes:clara-morel.md), its details are added to the post’s structured data. - Categories and tags create their own list pages,
/blog/category/<name>/and/blog/tag/<name>/.showCategories: trueinblog/english/-index.mdshows category buttons above the grid. - The post list.
/blog/shows the newest posts as large photo cards, then the rest in a 3-column grid. Inblog/english/-index.md,options.featuredis the number of photo cards on the first page (0hides them), andoptions.relatedLimitthe number of related posts under each article (0hides them). - Posts per page:
paginationinconfig.toml. - RSS (
/rss.xml, and/fr/rss.xml), reading time, and the Copy page links are automatic.
Team
The team page shows every item of list in src/content/team/english/-index.md. Members have no page of their own. image is a portrait (the demo uses 450×500 images). A member can have social links, where label is the icon name: Linkedin, Instagram, Facebook, X, or Youtube. The demo members have none.
Testimonials
Reviews live in one file, src/content/testimonial/english/-index.md, and are shown by the testimonials section on the main and EV homepages, the about page, and the services overview. Each review has the quote (content), a photo of the installed system (image, shown in the slider layout), and the customer’s name, role, and system (for example "7.2 kWp + battery"). The rating (1 to 5, default 5) sets the stars on each card in the wall layout, as on the about page; the other layouts don’t show it. options.limit in testimonial-section.md sets how many show, and summary the overall rating line. A page can show its own reviews instead with a list in its testimonialSection block, as the renewable energy homepage does (see Section Layouts).
Do It With AI
Add a [service | project | job opening | blog post | team member | testimonial] tosrc/content/[folder]/english/ for: [DETAILS]. Copy the frontmatter keys of an existing entry inthat folder, keep its comments, and fill in my values only. Use a customSlug (or file name) thatmakes a good URL. Then create a version in each other language folder of src/content/[folder]/with the same customSlug.