Removed blocking APP_INITIALIZER providers for config.json and station config
Config loads now fire asynchronously in App.ngOnInit() (browser platform only)
During SSR/prerender, the page renders instantly with default config — no network wait
This means crawlers get a fully rendered page without waiting for API responses
Impact
SSR/prerender: Build emits default-content HTML instantly — no HTTP calls during route extraction
SEO: Search crawlers see a fully rendered page without waiting for network
LCP: Hero background image paints faster because JS starts sooner
TTFB: Initial paint no longer blocked by config.json or station-config API calls
Testing
Build succeeds with both browser and server bundles
All 54 unit tests pass
Prerendered output confirmed for all 5 public routes
Preload tag present in prerendered HTML output
## Summary
Implements Phase 3 of the SEO audit recommendations — rendering architecture optimizations that improve initial paint speed and Core Web Vitals.
### Changes
#### 1. Preload LCP Image (src/index.html)
- Added `<link rel="preload" as="image" href="svg/mountain-landscape.svg">` for the hero background image
- The hero background is the Largest Contentful Paint (LCP) element on the landing page
- Preloading ensures the browser fetches it early, reducing LCP time
- Uses the default SVG as a fallback; runtime station config replaces it after hydration
#### 2. Reduce APP_INITIALIZER Blocking (src/app/app.config.ts, src/app/app.ts)
- Removed blocking `APP_INITIALIZER` providers for config.json and station config
- Config loads now fire asynchronously in `App.ngOnInit()` (browser platform only)
- During SSR/prerender, the page renders instantly with default config — no network wait
- This means crawlers get a fully rendered page without waiting for API responses
### Impact
- **SSR/prerender**: Build emits default-content HTML instantly — no HTTP calls during route extraction
- **SEO**: Search crawlers see a fully rendered page without waiting for network
- **LCP**: Hero background image paints faster because JS starts sooner
- **TTFB**: Initial paint no longer blocked by config.json or station-config API calls
### Testing
- Build succeeds with both browser and server bundles
- All 54 unit tests pass
- Prerendered output confirmed for all 5 public routes
- Preload tag present in prerendered HTML output
- Install @angular/ssr and @angular/platform-server (v21.2.x)
- Create server entry point (server.ts) and server bootstrap (main.server.ts)
- Add server routes config (app.server.routes.ts) with 5 prerendered routes:
/, /about, /schedule, /events, /contact
- Update angular.json with server/ssr/prerender build configuration
- Add prerender-routes.txt for build-time static route generation
- Fix SSR compatibility:
- App component: guard DOM access with isPlatformBrowser
- StructuredDataComponent: skip head manipulation on server
- ThemeService: skip CSS variable application on server
- ShowService/EventService: return empty arrays on API failure
(graceful fallback during prerender with no backend)
- Prerendered output verified: 5 static HTML routes in dist/
The PlatformLocation static initializer in @angular/common/router
requires @angular/compiler to be loaded first. Move the compiler
import before any Angular router/common imports so the JIT facade
is available when the module loads.
The SSR packages were pinned to ^21.2.19 and ^21.2.20 which had
strict peer dependency requirements conflicting with @angular/common
at ^21.2.0. This caused npm ci (used in CI) to fail with peer
dependency resolution errors.
Aligned both packages to ^21.2.0 to match the rest of the Angular
core packages, and regenerated package-lock.json.
- ThemeService: remove isPlatformBrowser guard so CSS custom properties
are applied during SSR/prerender — server-rendered HTML now carries the
correct color scheme and gradients (fixes missing colors on test site).
- main.ts: add provideClientHydration() so Angular preserves the server-
rendered DOM on the client instead of destroying and rebuilding it
(fixes jarring full-page reload between static and dynamic versions).
- Tests: update theme.service.spec.ts to match new behaviour, expand
coverage with gradient tests and empty-value handling (9 tests).
Fix structured-data.component.spec.ts import paths. All 54 tests pass.
- Add AngularAppEngine.ɵallowStaticRouteRender flag so the engine serves
prerendered static HTML from disk instead of falling back to SSR
- Rename default export to named const (requestHandler) to avoid self-import
loop when the module is the entry point
- Use requestHandler directly in the main-module listener instead of
importing './server.js' which caused TS2307 resolution failure
- Add <link rel="preload"> for hero background image (LCP element) in index.html
- Remove blocking APP_INITIALIZER for config.json and station config loading
- Config loads now fire asynchronously in App.ngOnInit() (browser only)
- SSR/prerender renders instantly with default config — no network wait
- Improves LCP, Time to First Byte, and initial paint for SEO
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Implements Phase 3 of the SEO audit recommendations — rendering architecture optimizations that improve initial paint speed and Core Web Vitals.
Changes
1. Preload LCP Image (src/index.html)
<link rel="preload" as="image" href="svg/mountain-landscape.svg">for the hero background image2. Reduce APP_INITIALIZER Blocking (src/app/app.config.ts, src/app/app.ts)
APP_INITIALIZERproviders for config.json and station configApp.ngOnInit()(browser platform only)Impact
Testing
- Install @angular/ssr and @angular/platform-server (v21.2.x) - Create server entry point (server.ts) and server bootstrap (main.server.ts) - Add server routes config (app.server.routes.ts) with 5 prerendered routes: /, /about, /schedule, /events, /contact - Update angular.json with server/ssr/prerender build configuration - Add prerender-routes.txt for build-time static route generation - Fix SSR compatibility: - App component: guard DOM access with isPlatformBrowser - StructuredDataComponent: skip head manipulation on server - ThemeService: skip CSS variable application on server - ShowService/EventService: return empty arrays on API failure (graceful fallback during prerender with no backend) - Prerendered output verified: 5 static HTML routes in dist/- Replace await import('./main.server.js') with lazy getEngine() (original path was wrong — main.server.ts lives in src/, not root) - AngularNodeAppEngine handles manifest registration internally; no manual bootstrap import needed - Add npm scripts: build:ssr, build:prerender, serve:ssr:radio-station - Add PRERENDER.md build documentation Verified: clean build prerenders all 5 routes, 54 tests pass