Skip to content

Inject styles from dynamically imported components on first dev server load#15988

Merged
matthewp merged 4 commits intowithastro:mainfrom
ossaidqadri:fix/15983-dynamic-import-css
Mar 19, 2026
Merged

Inject styles from dynamically imported components on first dev server load#15988
matthewp merged 4 commits intowithastro:mainfrom
ossaidqadri:fix/15983-dynamic-import-css

Conversation

@ossaidqadri
Copy link
Copy Markdown
Contributor

@ossaidqadri ossaidqadri commented Mar 19, 2026

Changes

  • Styles from components loaded via await import() in frontmatter are now injected on the first page request in dev mode. Previously they only appeared after a file save triggered HMR — a regression from Astro 5.
  • The root cause was in collectCSSWithOrder(): dynamically imported modules are registered in the Vite module graph by Vite's import analysis but not yet transformed, so their importedModules set is empty and the CSS walk terminates early. Added ensureModulesLoaded() which recursively walks the graph and eagerly fetches any untransformed modules via env.fetchModule() before the CSS collection walk runs.
  • ensureModulesLoaded() mirrors the same PROPAGATED_ASSET_QUERY_PARAM stopping point used by collectCSSWithOrder(), so it won't descend into propagated asset modules that the CSS walk would intentionally skip — avoiding unnecessary fetches in content collection graphs.

Testing

  • Added a dev mode integration test (test/css-dynamic-import-dev.test.js) with a fixture whose index page dynamically imports a layout component containing a <style> block. The test starts a dev server, fetches / on first load, and asserts the style is present in the HTML — directly covering the reported regression.
  • Existing css-order-import tests (including the dynamic import build scenario) continue to pass.

Docs

  • No docs update needed — dynamic imports in frontmatter were already a supported pattern; this restores the expected behavior.

Fixes #15983

… dev load

In Astro 6, `collectCSSWithOrder()` walks the Vite module graph to find CSS
dependencies, but dynamically imported modules are registered in the graph
without being transformed yet — their `importedModules` set is empty, so the
CSS walk terminates prematurely and their styles are never injected.

Add `ensureModulesLoaded()` which recursively walks the graph and eagerly
fetches any untransformed modules via `env.fetchModule()` before the CSS
collection walk runs. This populates each module's `importedModules` set,
making CSS dependencies from dynamically imported components visible on the
first page load.

Fixes withastro#15983
Copilot AI review requested due to automatic review settings March 19, 2026 01:14
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 19, 2026

🦋 Changeset detected

Latest commit: 78e94a9

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

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

@github-actions github-actions bot added the pkg: astro Related to the core `astro` package (scope) label Mar 19, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a dev-mode regression where CSS from components loaded via await import() in Astro frontmatter was not injected on the first page request, restoring behavior consistent with Astro 5.

Changes:

  • Add an ensureModulesLoaded() pre-walk that eagerly fetches/transforms reachable (previously untransformed) Vite modules before collecting CSS dependencies.
  • Add a dev-mode integration test + fixture to assert dynamic-imported component styles are present on the first dev server load.
  • Add a changeset describing the patch release.

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/astro/src/vite-plugin-css/index.ts Preloads/transforms reachable module graph nodes before the CSS dependency walk in dev.
packages/astro/test/css-dynamic-import-dev.test.js New integration test asserting styles from a dynamically imported layout are present on first load.
packages/astro/test/fixtures/css-dynamic-import-dev/package.json Fixture package manifest for the new dev-mode regression test.
packages/astro/test/fixtures/css-dynamic-import-dev/src/pages/index.astro Fixture page that dynamically imports a layout in frontmatter.
packages/astro/test/fixtures/css-dynamic-import-dev/src/layouts/Layout.astro Fixture layout containing a <style> block that must be injected on first request.
.gitignore Adds .claude to ignored files.
.changeset/fix-dynamic-import-css-dev.md Patch changeset documenting the regression fix.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +49 to +54
if (seen.has(id)) return;
seen.add(id);

for (const imp of mod.importedModules) {
if (!imp.id) continue;
if (seen.has(imp.id)) continue;
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Mar 19, 2026

Merging this PR will not alter performance

✅ 18 untouched benchmarks


Comparing ossaidqadri:fix/15983-dynamic-import-css (78e94a9) with main (9685e2d)

Open in CodSpeed

@matthewp matthewp merged commit c93b4a0 into withastro:main Mar 19, 2026
43 of 45 checks passed
@astrobot-houston astrobot-houston mentioned this pull request Mar 19, 2026
dadezzz pushed a commit to dadezzz/ice-notes that referenced this pull request Mar 24, 2026
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [astro](https://astro.build) ([source](https://github.com/withastro/astro/tree/HEAD/packages/astro)) | [`6.0.6` → `6.0.8`](https://renovatebot.com/diffs/npm/astro/6.0.6/6.0.8) | ![age](https://developer.mend.io/api/mc/badges/age/npm/astro/6.0.8?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/astro/6.0.6/6.0.8?slim=true) |

---

### Release Notes

<details>
<summary>withastro/astro (astro)</summary>

### [`v6.0.8`](https://github.com/withastro/astro/blob/HEAD/packages/astro/CHANGELOG.md#608)

[Compare Source](https://github.com/withastro/astro/compare/astro@6.0.7...astro@6.0.8)

##### Patch Changes

- [#&#8203;15978](withastro/astro#15978) [`6d182fe`](withastro/astro@6d182fe) Thanks [@&#8203;seroperson](https://github.com/seroperson)! - Fixes a bug where Astro Actions didn't properly support nested object properties, causing problems when users used zod functions such as `superRefine` or `discriminatedUnion`.

- [#&#8203;16011](withastro/astro#16011) [`e752170`](withastro/astro@e752170) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes a dev server hang on the first request when using the Cloudflare adapter

- [#&#8203;15997](withastro/astro#15997) [`1fddff7`](withastro/astro@1fddff7) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Fixes `Astro.rewrite()` failing when the target path contains duplicate slashes (e.g. `//about`). The duplicate slashes are now collapsed before URL parsing, preventing them from being interpreted as a protocol-relative URL.

### [`v6.0.7`](https://github.com/withastro/astro/blob/HEAD/packages/astro/CHANGELOG.md#607)

[Compare Source](https://github.com/withastro/astro/compare/astro@6.0.6...astro@6.0.7)

##### Patch Changes

- [#&#8203;15950](withastro/astro#15950) [`acce5e8`](withastro/astro@acce5e8) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes a build regression in projects with multiple frontend integrations where `server:defer` server islands could fail at runtime when all pages are prerendered.

- [#&#8203;15988](withastro/astro#15988) [`c93b4a0`](withastro/astro@c93b4a0) Thanks [@&#8203;ossaidqadri](https://github.com/ossaidqadri)! - Fix styles from dynamically imported components not being injected on first dev server load.

- [#&#8203;15968](withastro/astro#15968) [`3e7a9d5`](withastro/astro@3e7a9d5) Thanks [@&#8203;chasemccoy](https://github.com/chasemccoy)! - Fixes `renderMarkdown` in custom content loaders not resolving images in markdown content. Images referenced in markdown processed by `renderMarkdown` are now correctly optimized, matching the behavior of the built-in `glob()` loader.

- [#&#8203;15990](withastro/astro#15990) [`1e6017f`](withastro/astro@1e6017f) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Fixes an issue where `Astro.currentLocale` would always be the default locale instead of the actual one when using a dynamic route like `[locale].astro` or `[locale]/index.astro`. It now resolves to the correct locale from the URL.

- [#&#8203;15990](withastro/astro#15990) [`1e6017f`](withastro/astro@1e6017f) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Fixes an issue where visiting an invalid locale URL (e.g. `/asdf/`) would show the content of a dynamic `[locale]` page with a 404 status code, instead of showing your custom 404 page. Now, the correct 404 page is rendered when the locale in the URL doesn't match any configured locale.

- [#&#8203;15960](withastro/astro#15960) [`1d84020`](withastro/astro@1d84020) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes Cloudflare dev server islands with `prerenderEnvironment: 'node'` by sharing the serialized manifest encryption key across dev environments and routing server island requests through the SSR runtime.

- [#&#8203;15735](withastro/astro#15735) [`9685e2d`](withastro/astro@9685e2d) Thanks [@&#8203;fa-sharp](https://github.com/fa-sharp)! - Fixes an EventEmitter memory leak when serving static pages from Node.js middleware.

  When using the middleware handler, requests that were being passed on to Express / Fastify (e.g. static files / pre-rendered pages / etc.) weren't cleaning up socket listeners before calling `next()`, causing a memory leak warning. This fix makes sure to run the cleanup before calling `next()`.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My43Ni4yIiwidXBkYXRlZEluVmVyIjoiNDMuODYuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: Renovate Bot <renovate@zarantonello.dev>
Co-committed-by: Renovate Bot <renovate@zarantonello.dev>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Styles broken in componentes when using dynamic import

3 participants