# 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
<Steps>

1. Copy an existing entry in the folder, for example `src/content/projects/english/project-1.mdx`.
2. Rename the copy and change its `customSlug`, which sets the URL (`/projects/my-new-project/`). Collections whose entries have no `customSlug` use the file name.
3. Change the frontmatter values and the Markdown body. Keep the keys.
4. Add the same file in each other language folder with the translated text and the **same** `customSlug`.

</Steps>

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.

- `icon` is a [Lucide](https://lucide.dev/icons/) icon name in PascalCase, such as `"BatteryCharging"`.
- `image` is the photo on the card and at the top of the service page.
- `weight` sets the order. The homepages show the first 4, the overview the first 6 (`options.limit` in each `servicesSection`), 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](/ecozen/customization/writing-content/)).
- The page header badge comes from `badgeSecondary` in `services/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:

```yaml
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-12
weight: 1
categories:
  - Residential            # also the badge on the project page
information:               # 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`.

<Aside type="caution">
  The sample projects, their figures, and the quotes are invented. Replace them
  with your own installs, or delete them, before you launch.
</Aside>

### Careers
One file per open role, for example `src/content/careers/english/electrician.md`:

- `department` groups roles on `/careers/` (set `groupByDepartment: false` in `careers-list-section.md` for one flat list); `weight` orders them.
- `type` is `full-time`, `part-time`, or `contract`. The labels shown for each type are in `labels.types` of `careers/english/-index.md`.
- `location`, `type`, and `salary` appear as chips under the title. `salary` is free text, such as `"$34 to $42 per hour"`.
- `responsibilities`, `requirements`, and `benefits` are lists, shown under the headings set in `labels`.
- The **Apply** card comes from `apply` in `careers/english/-index.md`. Its button ships as `mailto:hello@example.com`: point it at your email, form, or hiring system. A role can set its own `button` to override it.
- `date`, `validThrough`, `location`, `type`, and `salary` feed 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 `.mdx` files with `title`, `customSlug`, `metaDescription`, `image`, `date`, `author`, `categories`, `tags`, and `draft`.
- **Author.** `author: "Clara Morel"` is shown under the title. If a matching file exists in `src/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: true` in `blog/english/-index.md` shows 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. In `blog/english/-index.md`, `options.featured` is the number of photo cards on the first page (`0` hides them), and `options.relatedLimit` the number of related posts under each article (`0` hides them).
- **Posts per page**: `pagination` in `config.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](/ecozen/customization/section-layouts/#testimonials)).

<Aside type="caution">
  Use real reviews only, with permission. Replace the sample reviews and the
  summary rating ("4.9, based on 600+ verified reviews") before launch.
</Aside>

### Do It With AI
```text
Add a [service | project | job opening | blog post | team member | testimonial] to
src/content/[folder]/english/ for: [DETAILS]. Copy the frontmatter keys of an existing entry in
that folder, keep its comments, and fill in my values only. Use a customSlug (or file name) that
makes a good URL. Then create a version in each other language folder of src/content/[folder]/
with the same customSlug.
```