---
title: Versions
description: 'Serve several versions of your docs side by side, each in its own folder, and start the next one by copying the current one with one command.'
url: 'https://larecipe.saleem.dev/3.x/writing/versions'
version: 3.x
locale: en
last_updated: '2026-10-18'
---

> Docs index: https://larecipe.saleem.dev/3.x/llms.txt · MCP: https://larecipe.saleem.dev/mcp · Skill: https://larecipe.saleem.dev/skill.md

# Versions

A doc set can serve the docs for the release people use today next to the docs for the one before it. List the versions in the set's `larecipe.json`, and give each one a folder named after its id:

```json title="docs/larecipe.json"
{
  "versions": [
    { "id": "3.x", "default": true },
    { "id": "2.x", "status": "maintained" }
  ]
}
```

- docs/
  - larecipe.json
  - 3.x/
    - navigation.json
    - index.md
  - 2.x/
    - navigation.json
    - index.md

Each version gets its own URLs (`/docs/3.x/installation`), navigation, search index and `llms.txt`, and readers switch between them in the header. A set without versions works too: its pages sit directly in `docs/` until you add the first version.

This site is an example: it serves `3.x`, the current docs, and `2.x`, the LaRecipe 2 docs converted by the [v2 upgrade](https://larecipe.saleem.dev/3.x/upgrade.md).

## Add a version

Copy the current docs into a new version, and make it the default:

```bash
php artisan larecipe:make:version 4.x --default
```

It copies the default version's folder and lists the new version in `larecipe.json`. Pass `--from=3.x` to copy another version, and `--dry-run` to see the files it would write. Then edit the pages that changed, and validate that version only:

```bash
php artisan larecipe:validate --docs-version=4.x
```

When the set has no versions yet, name the version its current pages belong to with `--current-as`. The command moves them into that folder before it copies:

```bash
php artisan larecipe:make:version 2.x --current-as=1.x --default
```

`larecipe.json`, `theme.css` and the `skills/` folder stay at the root: they belong to the set, not to a version. The command works on repository sets; a database set adds versions through its manifest, with the `update_manifest` MCP tool.

## Version keys

| Key | Default | Meaning |
| --- | --- | --- |
| `id` | required | The folder name and the URL segment, for example `3.x`. |
| `label` | the `id` | What the version switcher shows. |
| `status` | `current` | `current`, `maintained`, `deprecated` or `archived`. `llms.txt` describes each version by it, so agents know which one to prefer. |
| `default` | `false` | The version readers land on. When no version says `true`, the first one is the default. |
| `visibility` | `public` | `private` versions need the `viewLarecipe` gate on every surface. See [gate blocks and private docs](https://larecipe.saleem.dev/3.x/writing/gate-blocks.md). |

A version can also be a plain string (`"versions": ["3.x", "2.x"]`) when it needs nothing but an id.

## Details

**URLs, old links and the switcher**

A versioned page lives at `/{prefix}/{version}/{path}`. On this site the set is mounted at the domain root, so the installation page is `/3.x/get-started/installation`, and its Markdown twin is the same URL with `.md`.

A URL without a version redirects to that page in the default version, so links written before your second version existed never break. The one exception is the root of a set mounted at the domain root: `/` renders the default version's landing page directly, which is how this site opens on the Introduction.

A [redirect](https://larecipe.saleem.dev/3.x/writing/navigation.md#redirects) whose `from` has no version applies in every version, and a `to` without a version keeps the reader in theirs. Write the version, as in `/3.x/setup`, to redirect one version only.

Each page's canonical URL is its own URL in its own version: a 2.x page is canonical at its 2.x address.

The switcher in the header lists every version by its `label`. Choosing one keeps you on the same page when that page exists in the other version, and takes you to that version's landing page when it does not.

**What agents get for each version**

- `/llms.txt` lists the versions with their status, then the default version's pages, so an agent reaches the current docs in one fetch.
- `/{version}/llms.txt` lists that version's pages, and `/{version}/llms-full.txt` holds their full text.
- The MCP tools `search_docs`, `read_page` and `list_pages` take a `version` argument, and use the default version without one.

On this site those are [the root index](https://larecipe.saleem.dev/llms.txt), [the 3.x index](https://larecipe.saleem.dev/3.x/llms.txt) and [the 2.x index](https://larecipe.saleem.dev/2.x/llms.txt).

**Every larecipe:make:version option**

`larecipe:make:version` reads both forms of the `versions` list, and writes it back the way you wrote it. Read live from this app:

**Command** `larecipe:make:version`: Add a docs version by copying an existing one

```bash
php artisan larecipe:make:version [--from [FROM]] [--default] [--current-as [CURRENT-AS]] [--set [SET]] [--dry-run] [--] <version>
```

Arguments:

| Argument | Description | Default |
| --- | --- | --- |
| `version` | The new version id, for example 3.x (required) |  |

Options:

| Option | Description | Default |
| --- | --- | --- |
| `--from=FROM` | Copy the pages of this version (default: the default version) |  |
| `--default` | Make the new version the default |  |
| `--current-as=CURRENT-AS` | For a set without versions yet, the version its current pages belong to |  |
| `--set=SET` | The doc set (default: the default set) |  |
| `--dry-run` | Show what would change without writing anything |  |

## Next

- [Languages](https://larecipe.saleem.dev/3.x/writing/languages.md): the same page in more than one language, inside each version.
- [Navigation](https://larecipe.saleem.dev/3.x/writing/navigation.md): one `navigation.json` per version.
- [larecipe.json reference](https://larecipe.saleem.dev/3.x/reference/manifest.md): every manifest key.
