Skip to content

Conversation

@matthewp
Copy link
Contributor

@matthewp matthewp commented Nov 28, 2025

Changes

  • Add legacy.collectionsBackwardsCompat?: boolean (defaults to false)
    • When false (default): strict v6 behavior (no old-style collections)
    • When true: allows old-style collections to be defined and processed
  • Allows collections without loaders.
  • Collections without loaders get type: 'content' by default (was throwing error)
  • Gets glob loader if using type: 'content' or type: 'data'

Testing

Tests added for the new flag.

Docs

TODO - what should be document outside of the flag?

…ctions migration

PROBLEM
-------
In v5, content collections could be defined without a loader and would work via
automatic backwards compatibility mode (supporting v4-style collections). This
allowed old code to continue working without explicit migration.

In v6 (current next branch), we completely removed this backwards compat layer.
Collections without loaders now throw an error in defineCollection(), and old-style
collections (type: 'content' or type: 'data') are silently skipped during sync.

This creates a harsh upgrade path: users upgrading to v6 get hard errors if they
have legacy collections. They don't realize they're using deprecated patterns
because their code 'just worked' in v5.

SOLUTION
--------
Add a legacy.collectionsBackwardsCompat flag that provides an escape hatch for v6.
This allows projects to opt-in to backwards compatibility while they migrate, and
signals explicitly that they're using deprecated patterns.

IMPLEMENTATION DETAILS
----------------------

1. Config Schema Changes
   File: packages/astro/src/types/public/config.ts
   File: packages/astro/src/core/config/schemas/base.ts

   - Add legacy.collectionsBackwardsCompat?: boolean (defaults to false)
   - When false (default): strict v6 behavior (no old-style collections)
   - When true: allows old-style collections to be defined and processed

2. defineCollection() Logic Restoration
   File: packages/astro/src/content/config.ts

   - Restore v5 logic that allows collections without loaders
   - Collections without loaders get type: 'content' by default (was throwing error)
   - Only validate loader presence IF a loader is defined in config
   - Changed from: 'must have loader' → to 'if loader exists, validate it'

   This means:
   - Old code like: defineCollection({ schema: z.object(...) })
     now works instead of throwing ContentCollectionMissingLoader error
   - Collections get default type: 'content' if not specified

3. Content Layer Processing
   File: packages/astro/src/content/content-layer.ts

   - Check this.#settings.config.legacy?.collectionsBackwardsCompat before syncing
   - If flag is FALSE (default):
     * Skip collections with type: 'content' or type: 'data'
     * Skip collections without loaders
     * Only process loader-based collections (v6 strict mode)

   - If flag is TRUE (backwards compat enabled):
     * Process both loader-based AND old-style collections
     * Old-style collections still need their files to exist in the right location
     * But they work without explicit loader definitions

   - Made all loader access safe with 'loader' in collection guards

MIGRATION PATH
---------------
For projects upgrading to v6 with legacy collections:

1. Add to astro.config.mjs: legacy: { collectionsBackwardsCompat: true }
2. Content will continue working (same as v5)
3. At leisure, migrate collections one-by-one:
   - Add glob() or file() loader
   - Change type: 'content' to type: 'content_layer' (or omit type)
   - Verify it works
4. Remove the legacy flag once all collections are migrated

NOTES
-----
- This is v6-specific. v5 users don't see this flag (it exists but defaults to false)
- The flag name signals intent: 'backwards compat' is deprecated, not a permanent feature
- In Astro 7, we can remove this flag entirely
- This enables a smoother upgrade experience without encouraging long-term reliance
  on legacy patterns
@changeset-bot
Copy link

changeset-bot bot commented Nov 28, 2025

⚠️ No Changeset found

Latest commit: 27570e0

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

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

@github-actions github-actions bot added pkg: astro Related to the core `astro` package (scope) docs pr labels Nov 28, 2025
@ascorbic
Copy link
Contributor

ascorbic commented Dec 1, 2025

To be clear, this just add a flag for the checks, but it doesn't actually reimplement any backwards-compat that was removed in #14407

@matthewp
Copy link
Contributor Author

matthewp commented Dec 3, 2025

@ascorbic Correct, what I read from the thread was that there was concern that people wouldn't know what they needed to upgrade, so I created #14928 to give them errors when they don't use loaders (in v5) and this was to allow them get the simpleLoader if they don't provide a loader themselves.

I didn't add back any more of the compat because it wasn't clear to me from the thread what backwards compat was desired.

@ascorbic
Copy link
Contributor

ascorbic commented Dec 8, 2025

I think the summary of backwards-compat behaviour that we should re-enable:

  • config file in src/content/config.ts.
  • collections with no loader, and/or with type: content or type: data. These would get the automatic glob loader in legacy mode, which generates entries that support the legacy API:
    • entry.slug
    • entry.render()

I'm not sure if we should also bring back getEntryById and getEntryBySlug

@sarah11918
Copy link
Member

sarah11918 commented Dec 8, 2025

I'm not sure if we should also bring back getEntryById and getEntryBySlug

These were already deprecated in Astro v4. I did a quick search on GitHub to see if I could find much use of them in Astro projects, and my advanced search probably wasn't working properly because it showed ZERO code matches, but scanning what was returned, there probably isn't much use in current projects.

So, probably safe to remove these, I think? We have also had GetEntryDeprecationError since v4, so this all seems safe. They would have been seeing errors about this for a while already.

Otherwise, Matt's suggestions LGTM!

@sarah11918
Copy link
Member

Will there be any complication with the move to Zod 4 here? Will backwards compatibility exist, but yet people will still have to upgrade their schemas to satisfy Zod 4? Or will the backwards compatibility include staying on Zod 3?

@ascorbic
Copy link
Contributor

ascorbic commented Dec 8, 2025

I don't think it would make any difference. It wouldn't touch the schema stuff – it's mostly related to loaders

@sarah11918
Copy link
Member

So then, if someone enables "backwards compatibility" they only get "backwards LOADER compatibility" but would still have to go through the Zod 4 update? That's more what I'm wondering. "Backwards compat" doesn't mean except from other Astro 6 upgrades?

@ascorbic
Copy link
Contributor

ascorbic commented Dec 8, 2025

Will there be no backwards-compat for Zod then?

@sarah11918
Copy link
Member

I don't know, but we don't mention any way to enable it in the upgrade guide entry Matthew submitted to docs, so I'm guessing no?

@matthewp
Copy link
Contributor Author

matthewp commented Dec 8, 2025

No, we can't support both Zod 3 and Zod 4, it was attempted but decided to just do Zod 4.

@github-actions
Copy link
Contributor

github-actions bot commented Dec 8, 2025

⚠️ Package Trust Level Decreased

Caution

Decreased trust levels may indicate a higher risk of supply chain attacks. Please review these changes carefully.

📦 Package 🔒 Before 🔓 After
@cloudflare/unenv-preset trusted-with-provenance none
workerd trusted-with-provenance none
ts-api-utils provenance none
undici provenance none
miniflare trusted-with-provenance none
youch provenance none
@cloudflare/workerd-darwin-64 trusted-with-provenance none
@cloudflare/workerd-darwin-arm64 trusted-with-provenance none
@cloudflare/workerd-linux-64 trusted-with-provenance none
@cloudflare/workerd-linux-arm64 trusted-with-provenance none
@cloudflare/workerd-windows-64 trusted-with-provenance none
wrangler trusted-with-provenance none

@matthewp
Copy link
Contributor Author

matthewp commented Dec 8, 2025

I've updated the PR to do these things:

  • Allow src/content/config.ts
  • Collections with no loader get the glob loader if using type: 'content' or type: 'data'.

That's all.

@ascorbic
Copy link
Contributor

Collections with no loader get the glob loader if using type: 'content' or type: 'data'.

It should also be if there is no type set, as it should default to content in that case. It looks like that is what's currently implemented, but I just wanted to flag it.

@matthewp matthewp marked this pull request as ready for review December 15, 2025 20:04
Copy link
Member

@ematipico ematipico left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not very familiar with this part of the code, so take my review with a huge grain of salt

Copy link
Member

@sarah11918 sarah11918 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just left a couple of comments with thoughts on the wording of some messages!

@matthewp matthewp merged commit a33e1c6 into next Jan 5, 2026
23 checks passed
@matthewp matthewp deleted the feat/restore-collections-backwards-compat branch January 5, 2026 14:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs pr pkg: astro Related to the core `astro` package (scope)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants