The number that matters
All-chunk transfer went from 4.02 MB to 4.19 MB. Almost nothing. Initial went from 6.80 / 1.14 MB to 12.43 / 2.00 MB. Monetize, insights, messages, heic-to, and IVS did not grow. The extra bytes were already in the app. They sat in lazy on their branch and sat in initial on yours.
Angular 20 did not blow the budget. The split they built got undone.
SharedModule is the whole trick
AppModule imports SharedModule. Anything that module imports is first-load JS, even if the template never shows it. They took a pile of vendors and feature widgets out of shared and left them on the routes that actually use them.
Gone from their shared module: ngx-intl-tel-input and libphonenumber, @ctrl/ngx-emoji-mart, ngx-color-picker, ngx-material-timepicker, ng-otp-input, NgIvsModule, share-buttons, angular-mentions, the three video players, ProfileComponent, post-submit, mobile-input, custom-multiselect.
Yours still imports all of that. That is why initial has chunk-SA2WMUN6.js at 675.29 kB and why the top unnamed initial chunks went from 4.18 MB to 9.31 MB. I would start here. The @defer wrappers are garnish until this graph is smaller.
What they bolted on after the slim
You have no @defer in the tree. They hid the leftovers. Phone, timepicker, color picker, mobile-input, custom-multiselect, the welcome-video modal, and the alert-modal carousel wait for on idle. The emoji picker is a tiny wrapper and only loads on immediate when it is open. Feed and profile videos, comments, and sponsored ads wait for on viewport.
Lara is the other one. Your AppModule does import * as LaraTheme from '@primeuix/themes/lara' and hands the preset to providePrimeNG. Every first paint pays for it. They register PrimeNG with darkModeSelector: false and nothing else. PrimeNgLoaderService dynamic-imports @primeng/themes/lara the first time image-overlay or custom-multiselect constructs.
The header also static-imports three generated marketing files on your side, about 906 kB of source. They await import() those same files in ngOnInit. The download still happens soon after boot. It is no longer in the initial graph Angular prints.
Your CSS work is the part I would keep
styles shrank from 1.85 MB to 1.81 MB, about 35 kB. ng-select, datepicker, and toastr are inject: false on yours and still sit in their main stylesheet. LazyVendorCssDirective plus the loadFor* calls on toastr, material, video, and datepicker cover more widgets than their helper list. Their loadForPrimeNG() only pulls primeicons. The theme arrives as JS.
That CSS path is why a PrimeNG carousel looks finished on your first paint, and why a prerendered page can already have the vendor <link> if the widget constructed on the server. They skip CSS on the server, then fire material, flags, and primeicons from app.component on a setTimeout(0). Fine for a SPA. Worse for HTML you already spent 8 minutes prerendering.
What a user actually sees
Phone fields on their branch sit empty until the browser is idle, then pop in. Both sides already wrap the control in *ngIf="cs.isBrowser", so SSR never had a real phone input anyway. You at least show it on the first client render.
Emoji is the dishonest one on your side. *ngIf hides the picker. PickerModule is still in the initial bundle. They pay for that JS when the user opens it.
Feed video is the one I do not fully trust on their branch. @defer (on viewport) plus a placeholder is the right idea, and taking the players out of SharedModule is why video.js is not in your 12.43 MB. I could not find VideoPlayerV2 imported into the module that declares post-card. CUSTOM_ELEMENTS_SCHEMA would swallow the compile error. Click a feed video on that branch before you copy the pattern. A missing import would make the size look great and the player never boot.
PrimeNG can flash unstyled on theirs until Lara lands. Yours never does. Form widgets on theirs go blank for a beat on idle. Header marketing links on theirs fill a tick late, which is why they added applyAsyncSafe() for SSR change detection. Yours has the data in the same module as the header, so the links are there.
If you port anything
Keep the inject: false list and the directive. Do not switch to their CSS loader. Take SharedModule apart the way they did, then add the wrappers and @defer so the leftovers cannot sneak back into initial through a template. Lara can stay eager if you hate the first-widget flash, or move behind PrimeNgLoaderService if you want their initial number more than their FOUC.
Server initial follows the same split, 5.92 MB against your 11.64 MB. Same cause. Same fix.
Q&A
-
Show answer
No. Feature lazy modules stayed flat. Total transfer only moved 0.17 MB. The Angular 20 QA build put Lara, marketing JSON, intl-tel, emoji, video, IVS, and the rest back on the
AppModulegraph. Their Angular 19 build had already taken those off it. -
Show answer
No. Those are independent.
inject: falseon ng-select, datepicker, and toastr is yours. Their mainstylesstill contains those three. Port the module graph. Keep the CSS loader.