Skip to content

feat(angular): allow per-component standalone import paths for better code-splitting - #31303

Open
OS-jacobbell wants to merge 24 commits into
major-9.0from
FW-6470
Open

feat(angular): allow per-component standalone import paths for better code-splitting#31303
OS-jacobbell wants to merge 24 commits into
major-9.0from
FW-6470

Conversation

@OS-jacobbell

@OS-jacobbell OS-jacobbell commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Issue number: resolves #30114


What is the current behavior?

Any standalone components imported by one page are bundled with every page. E.g. a landing page will deliver all Ionic Framework components used by the web app, even if most of them are not used by the landing page. Because all components are imported from one file, build tools don't split the code well.

What is the new behavior?

  • Components can be imported from individual files, allowing build tools to bundle some components only with certain pages. E.g. import { IonToolbar } from '@ionic/angular/ion-toolbar'
  • @ionic/angular is now a barrel file, maintaining backwards compatibility.
  • Replaced ng-packagr build system with calling ngc directly.
  • Changed standalone/src pattern to src/standalone.
  • Made packages/angular/package.json the package.json that will be used in the npm package, in line with the React and Vue packages.

Does this introduce a breaking change?

  • Yes
  • No

Other information

I'm unfamiliar with Angular npm package best practices, I just set up what seems reasonable to match the React and Vue ones. Also not sure if release scripts will need to be updated.

@OS-jacobbell
OS-jacobbell requested a review from a team as a code owner July 29, 2026 16:48
@OS-jacobbell
OS-jacobbell requested a review from BenOsodrac July 29, 2026 16:48
@vercel

vercel Bot commented Jul 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ionic-framework Ready Ready Preview Aug 13, 2026 7:16pm

Request Review

@github-actions github-actions Bot added package: core @ionic/core package package: angular @ionic/angular package labels Jul 29, 2026

const p = spawn(cmd, args, { cwd: typescriptPath, stdio: 'inherit', shell: true });
const typescriptPath = path.join(__dirname, '..', 'node_modules', '.bin');
const p = spawn(cmd, args, { cwd: typescriptPath, stdio: 'inherit'});

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removing shell: true fixes a deprecation warning when building.

@ShaneK ShaneK left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Great work! I built the package and bundled a landing route that imports only IonToolbar: it went from 608KB across 15 chunks, pulling in all 9 components the other route used, down to 47KB across 3 chunks with just ion-toolbar. About a 92% cut for that page. The win comes from code splitting rather than tree-shaking, a whole-app bundle only moves about 0.5%, but this was about code splitting, I'm only highlighting that so people don't get the wrong idea about this PR.

I also ran the ng18 and ng22 test apps and got 189/189 e2e passing on both, and lint and the generated-file diff check are clean.

There are a few things I'd want fixed before this merges though. The big one is ng add @ionic/angular being broken in two separate ways, and anyone on moduleResolution: node10 can't resolve the package at all anymore, which makes this a breaking change which should probably be indicated in the BREAKING.md and docs migration guide despite the checkbox. There's actually already a moduleResolution change indicated in the BREAKING.md and migration guide so this one can just be tacked on to that. There are also two dead entries in the exports map. The rest are smaller and some are just nits.

Comment thread packages/angular/package.json
Comment thread packages/angular/package.json Outdated
Comment thread packages/angular/package.json
Comment thread packages/angular/tsconfig.json
Comment thread packages/angular/tsconfig.json
Comment thread .github/workflows/release-ionic.yml
Comment thread packages/angular/test/base/scripts/sync.sh
Comment thread packages/angular/scripts/build-schematics.js Outdated
Comment thread packages/angular/package.json
Comment thread packages/angular/scripts/build-schematics.js Outdated
Co-authored-by: Shane <shane@shanessite.net>

@ShaneK ShaneK left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Great work! Everything from last round is fixed and I re-tested it all.

The code splitting works nicely. A landing route importing only IonToolbar comes out 3 chunks and 46.5 KB with 1 component, against 15 chunks and 605.6 KB with 9 through the barrel.

On the PURE workaround, I checked webpack too since that's what the old comment blamed. Importing one component from the barrel pulls in 0 others, so that worked nicely, good work!

There's still a couple of issues holding this up, but great progress so far!

Comment thread packages/angular/tsconfig.json Outdated
Comment thread packages/angular/package.json Outdated
Comment thread packages/angular/scripts/build-schematics.js Outdated
Comment thread packages/angular/scripts/verify-exports.js Outdated
Comment thread packages/angular/package.json
"prettier": "@ionic/prettier-config",
"schematics": "./schematics/collection.json"
"schematics": "./dist/schematics/collection.json",
"typings": "./dist/standalone/index.d.ts",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The main entry resolves under moduleResolution: node10 now, but the subpaths don't. Importing @ionic/angular/lazy or any per-component path gives TS2307. On major-9.0 all of ., /common and /lazy resolve, because ng-packagr drops a small package.json into each entry-point folder with module and typings in it and node10 follows those. This relies on the exports map alone, which node10 ignores.

I think this is fine to leave, node10 is deprecated in TS 6.0 and Angular 18+ wants bundler. Only mentioning it because the main entry got fixed for the same reason. The common one doesn't matter, the README already says not to import it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

What's the difference between this for node10 and documentation here? https://github.com/ionic-team/ionic-framework/blob/major-9.0/BREAKING.md?plain=1#L311

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nope, same thing, that note already covers it. It's more true now than when it was written, if anything, since those per-entry-point shims I mentioned are the bit that's disappearing.

One other thing I noticed while poking around in here: the module field is gone too. Anything that ignores exports and goes off mainFields used to find the root entry through fesm2022, and now it finds nothing at all. React and Vue both set main. You could add "module": "./dist/standalone/index.js" if you want, but honestly I'd put it in the same bucket as the node10 thing and just leave it.

@ShaneK ShaneK left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Awesome work! Just one actual blocker left

Comment thread packages/angular/package.json
Comment thread core/stencil.config.ts
"prettier": "@ionic/prettier-config",
"schematics": "./schematics/collection.json"
"schematics": "./dist/schematics/collection.json",
"typings": "./dist/standalone/index.d.ts",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nope, same thing, that note already covers it. It's more true now than when it was written, if anything, since those per-entry-point shims I mentioned are the bit that's disappearing.

One other thing I noticed while poking around in here: the module field is gone too. Anything that ignores exports and goes off mainFields used to find the root entry through fesm2022, and now it finds nothing at all. React and Vue both set main. You could add "module": "./dist/standalone/index.js" if you want, but honestly I'd put it in the same bucket as the node10 thing and just leave it.

@ShaneK ShaneK left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Awesome job on this! 🎉 One nit looks like it just comes from a bad merge resolution, the rest looks good!

Comment thread packages/angular/package.json Outdated
Co-authored-by: Shane <shane.king@outsystems.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

package: angular @ionic/angular package package: core @ionic/core package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants