Skip to content

feat: field placeholders in collection paths - #1649

Open
GiGwebs wants to merge 1 commit into
Thinkmill:mainfrom
GiGwebs:feat/collection-path-placeholders
Open

GiGwebs wants to merge 1 commit into
Thinkmill:mainfrom
GiGwebs:feat/collection-path-placeholders

Conversation

@GiGwebs

@GiGwebs GiGwebs commented Oct 27, 2026

Copy link
Copy Markdown

Solves #340 — more configurable collection paths (locales, publish dates, etc.).

Approach

Rather than bare path: (fields) => string callbacks, this implements {field} placeholders as full path segments — matching the direction discussed on the issue (field names embedded in the path keep entry listing statically knowable for the GitHub storage backend):

blog: collection({
  path: 'src/content/blog/{locale}/*',
  slugField: 'title',
  schema: { locale: fields.text({ label: 'Locale' }), title: fields.slug({ name: 'Title' }) },
})
  • Entry slug = pattern filled in (en/my-post) — same composite-slug model ** collections already use, so routing, URLs, the reader, and both storage backends work unchanged
  • Creation fills {x} segments from each field's serialized value (serializeWithSlug / serialize, primitives coerced); static mid-pattern segments ({x}/posts/*) work too
  • Editing a placeholder field moves the entry via existing slug-rename machinery
  • Fully backwards compatible: paths without { behave exactly as before; multi-glob paths now produce a clear error instead of silent corruption

Tests

  • New path-utils.test.ts: 14 tests — parsing/errors, schema validation, slug composition, tree listing at exact depth, reader list/read with composite slugs, wrong-depth rejection
  • vitest run: 1265 pass; tsc clean; build:packages OK

Notes

  • Placeholders can't appear after the glob (clear config error)
  • {x} values aren't slugified — segment-safety is enforced at save via field validation
  • Composite-slug uniqueness (en/foo ≠ fr/foo) and save-time placeholder validation are covered

@algora-pbc /claim #340

Allow path segments bound to schema fields: path: 'blog/{locale}/*'.
Placeholder fields are filled from serialized field values at entry
creation; editing moves the entry via slug-rename. Backwards compatible
— paths without { are unchanged. Multi-glob paths now error clearly.

Solves Thinkmill#340.
@GiGwebs
GiGwebs requested a review from emmatown as a code owner October 27, 2026 14:25
@changeset-bot

changeset-bot Bot commented Oct 27, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8561ca0

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@keystatic/core Minor
@keystatic/templates-astro Patch
@keystatic/templates-nextjs Patch
@keystatic/templates-remix Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant