Skip to content
New: Customize your theme with an AI assistant. Read the guide

Writing Content

Skill level: Beginner · Used in: blog posts, service and area pages, legal and text pages

Every content file has two parts: the frontmatter between the two --- lines at the top (settings and short texts, written in YAML), and the body below it (longer text, written in Markdown). Files that end in .mdx can also use shortcodes: small components such as callouts, checklists, and buttons.

---
title: "Cancellation policy" # frontmatter: YAML
description: "When you can change or cancel a visit."
date: 2026-09-01
draft: false
---
## Changing a visit ← body: Markdown
Free up to 24 hours before your arrival window.

In Homio, blog posts are .mdx files. Services, areas, projects, and legal pages are .md files, so they can use Markdown and the business tokens below, but not shortcodes.

Frontmatter Basics

  • Keep the keys exactly as they are; change only the values.
  • Put text in double quotes: title: "Deep clean". Write a quote inside text as \" or use single quotes around the text.
  • Dates are YYYY-MM-DD: date: 2026-09-08.
  • Lists start each item with - (or use ["a", "b"]), and nested keys are indented with two spaces. Copy the indentation of the item above.
  • src/content/_file_format.md lists the common page fields with a comment on each.

File Formats explains YAML, Markdown, and TOML for beginners.

The# sampleComments

Text after # on a YAML line is a comment: the site ignores it. Homio uses comments in two ways:

  • To list options. layout: "default" # default | list tells you the allowed values.

  • To mark sample facts. Values you must replace before launch end with # sample:

    - value: 38 # sample
    label: "Crew members"

Search the project for # sample to find every sample price, rating, count, promise, phone number, and date. Replace the value and delete the comment once the fact is yours. If you can’t confirm a figure, remove the item instead of keeping an invented number.

Markdown Basics

## A section heading
### A smaller heading
Plain text. **Bold**, _italic_, ~~struck through~~, and [a link](/pricing/).
- A list item
- Another item
1. A numbered step
2. Another step
> A quote from a client or a crew member.
| Room | Time |
| -------- | ------ |
| Kitchen | 2 h |
  • Start headings at ##: the page title is already the # heading. Posts get a table of contents from their headings ([settings.markup.tableOfContents] in config.toml).
  • Links to other sites open in a new tab automatically.
  • Code blocks with three backticks get syntax colours and a copy button; [settings.markup.codeBlocks] theme is auto (follows light and dark mode) or dark.

Heading Sizes

Add a class in square brackets at the end of a heading to set its size without changing its level:

## A door that sticks: three causes [.h4]
### Before the crew arrives [.h5]

The heading stays an h2 or h3 for search engines and screen readers, but is drawn at the h4 or h5 size.

Business Details In Text

In .md files, these tokens are replaced with your details from [business] in config.toml, so you never type them twice:

TokenBecomes
{name}The business name
{phone}The phone number as shown
{email}The email address
{address}The address
{hours}The opening hours
Call {phone} or email {email} to change a booking.

In .mdx files, { starts code, so the tokens don’t work there: write the details out, or put the text in a .md file. Form messages and several section fields (for example "Call {phone}" in a form error) also accept them, as noted in their comments.

Images In The Body

  • Markdown images with a path that starts with / point to the public/ folder: ![Alt text](/images/gallery/1.jpg) is public/images/gallery/1.jpg, served as it is.
  • A relative path to src/assets/images/ is optimized at build time, as in components.mdx: ![Close-up of carpet fibres](../../../assets/images/blog/carpet-texture.jpg).
  • Frontmatter image fields such as image work differently: "/images/blog/door-hinge-fix.jpg" reads src/assets/images/blog/door-hinge-fix.jpg and is optimized. See Icons & Images.

Shortcodes

These shortcodes work in every .mdx file without an import. The list is src/lib/shortcodes.ts, and the /components/ page shows each one with its code (source: src/content/pages/english/components.mdx).

Callout: a tip or a warning pulled out of the text. type is note (default), tip, warning, or danger. Without a title, the type name is shown. The older name <Notice> still works.

<Callout type="tip" title="Test on a hidden spot first">
Try any cleaner inside a cupboard door before you use it on the whole surface.
</Callout>

Steps: numbers the headings inside it. Each ## or ### heading starts a step.

<Steps>
### Find where it rubs
Close the door slowly and look for the worn patch on the frame.
### Tighten the top hinge
Use a hand screwdriver, not a drill.
</Steps>

ListCheck: turns a Markdown list into a checklist. columns={2} shows two columns on wider screens.

<ListCheck columns={2}>
- **Oven and hob**: the first door most inspectors open.
- **Fridge and freezer**: defrosted, dry, door left open.
</ListCheck>

JobTicket: a checklist drawn as a job ticket. With service and no total, it shows that service’s from-price from the rate card and a Get a price button.

<JobTicket title="Before the crew arrives" code="TIP-021" label="Deep clean" service="deep-cleaning">
- Clear the kitchen counters
- Leave a key or a door code
</JobTicket>

PriceCta: the price band with a service’s from-price and a button to the estimator. Blog posts add it on their own from the service field in their frontmatter; use the shortcode elsewhere.

<PriceCta service="window-cleaning" />

Button: a theme button, with the same options as buttons in content files (label, url, variant: fill, soft, outline, accent, fill-white, or text).

<Button label="Get a price" url="/book/" />
<Button variant="outline" label="Call us" url="/contact/" />

Accordion: a question that opens. Rows with the same group close each other; expanded="true" opens one on load.

<Accordion label="Does someone have to be in during the visit?" group="visit" expanded="true">
No. Most clients leave a key or a door code.
</Accordion>

Tabs: content in tabs.

<Tabs>
<Tab name="Kitchen">Degrease the hood filter and wipe the cabinet fronts.</Tab>
<Tab name="Bathroom">Descale the taps and the shower screen.</Tab>
</Tabs>

ImageList and ImageItem: photos in a grid with an optional caption. width="1/2" or "1/3" sets the share of the row; videoSrc adds a play button.

<ImageList>
<ImageItem imageSrc="/images/blog/door-hinge-fix.jpg" imageAlt="Screwdriver on a door hinge" caption="Tighten the top hinge first." />
</ImageList>

VideoInline: a video in the text. provider is youtube (the video id), vimeo, or html5 (a file in public/videos/, with an optional poster). YouTube videos use the privacy-friendly youtube-nocookie.com player.

<VideoInline src="rFVpSwgCkCo" provider="youtube" />

Drafts

draft: true in the frontmatter keeps a page or entry out of the built site, the lists, the sitemap, and llms.txt. It still shows in npm run dev, so you can keep working on it. A draft: true in a collection’s -index.md hides the whole collection. The four homepages don’t read draft: delete their files to remove them.

updated: 2026-10-01 marks the last meaningful edit: posts show it as “Updated”, and it is used in structured data and the sitemap.

Do It With AI

Write a blog post about [TOPIC] as an .mdx file in src/content/blog/english/. Match the
frontmatter of the other posts there (title, description, image, imageAlt, date, author,
categories, tags, service, draft). Use ## headings, one <Callout type="tip"> and one <ListCheck>
where useful, and set service to the rate-card id of [SERVICE] so the price band shows. Keep it
under [N] words and use only facts I give you. Then write the French version in
src/content/blog/french/ with the same file name.
Type something to search...