ngxsmk-datepicker
Version:
<!-- SEO Keywords: Angular DatePicker, Angular Date Range Picker, Lightweight Calendar Component, Angular Signals DatePicker, SSR Ready DatePicker, Zoneless Angular, A11y DatePicker, Mobile-Friendly DatePicker, Ionic DatePicker Meta Description: The
305 lines (223 loc) • 7.94 kB
Markdown
# Server-Side Rendering (SSR) Guide
**Last updated:** March 21, 2026 · **Current stable:** v2.2.8
ngxsmk-datepicker is fully compatible with Angular Universal and server-side rendering. This guide covers SSR setup, best practices, and troubleshooting.
## Overview
The datepicker is designed to work seamlessly in both server and browser environments:
- ✅ All browser-only APIs are guarded with platform checks
- ✅ No direct `window` or `document` access during initialization
- ✅ Event listeners only attach in browser environment
- ✅ Compatible with Angular Universal
- ✅ Supports partial hydration
## Basic SSR Setup
### Angular Universal Installation
If you haven't set up Angular Universal yet:
```bash
ng add @nguniversal/express-engine
```
### No Additional Configuration Required
The datepicker works out of the box with SSR. No special configuration is needed:
```typescript
import { Component } from '@angular/core';
import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
@Component({
selector: 'app-root',
standalone: true,
imports: [NgxsmkDatepickerComponent],
template: `
<ngxsmk-datepicker
mode="single"
[value]="selectedDate">
</ngxsmk-datepicker>
`
})
export class AppComponent {
selectedDate: Date | null = null;
}
```
## Platform Detection
The datepicker automatically detects the platform and only uses browser APIs when available:
```typescript
// Internal implementation (you don't need to do this)
private readonly isBrowser = isPlatformBrowser(this.platformId);
// Browser-only code is automatically guarded
if (this.isBrowser) {
// Safe to use window, document, etc.
}
```
## SSR Best Practices
### 1. Initialize Values Safely
Always initialize date values safely:
```typescript
import { Component, PLATFORM_ID, inject } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
@Component({
// ...
})
export class SafeComponent {
private platformId = inject(PLATFORM_ID);
selectedDate: Date | null = null;
ngOnInit() {
// Only access browser APIs in browser
if (isPlatformBrowser(this.platformId)) {
this.selectedDate = new Date();
}
}
}
```
### 2. Use Signals for Reactive Data
Signals work great with SSR:
```typescript
import { Component, signal } from '@angular/core';
@Component({
// ...
})
export class SignalComponent {
// Signals are SSR-safe
selectedDate = signal<Date | null>(null);
}
```
### 3. Server-Side Data
When fetching data on the server:
```typescript
import { Component, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { httpResource } from '@angular/common/http';
import { signal, linkedSignal, computed } from '@angular/core';
@Component({
// ...
})
export class ServerDataComponent {
private http = inject(HttpClient);
resource = httpResource({
request: () => this.http.get<{ date: string }>('/api/data'),
loader: signal(false)
});
localObject = linkedSignal(() => this.resource.response.value());
// Computed signals are SSR-safe
selectedDate = computed(() => {
const obj = this.localObject();
return obj?.date ? new Date(obj.date) : null;
});
}
```
## Testing SSR
### Build for SSR
```bash
npm run build:ssr
npm run serve:ssr
```
### Verify SSR Works
1. Build your app with SSR
2. Check the server-rendered HTML contains the datepicker markup
3. Verify no console errors during server rendering
4. Test that the datepicker works after hydration
## Common SSR Issues
### Issue: Datepicker not rendering on server
**Solution**: Ensure you're using the component correctly. The datepicker renders its initial state on the server, but interactive features (like opening the calendar) only work in the browser.
### Issue: `window is not defined` error
**Solution**: This shouldn't happen with the current version. If you see this error, ensure you're using the latest version of the datepicker. All browser APIs are properly guarded.
### Issue: Dates not displaying correctly
**Solution**: Ensure date values are properly serialized/deserialized. Use `Date` objects, not strings, when possible:
```typescript
// ❌ Bad - may cause issues
date: "2024-01-15"
// ✅ Good
date: new Date("2024-01-15")
```
### Issue: Locale not working on server
**Solution**: The datepicker uses the browser's locale by default. On the server, you may need to explicitly set the locale:
```html
<ngxsmk-datepicker
[locale]="'en-US'"
mode="single">
</ngxsmk-datepicker>
```
Or inject `LOCALE_ID`:
```typescript
import { LOCALE_ID, inject } from '@angular/core';
@Component({
// ...
providers: [
{ provide: LOCALE_ID, useValue: 'en-US' }
]
})
export class LocaleComponent {
locale = inject(LOCALE_ID);
}
```
## Hydration
The datepicker supports Angular's partial hydration:
1. The component renders on the server
2. On client hydration, interactive features become available
3. No additional configuration needed
## Performance Considerations
### OnPush Change Detection
The datepicker uses `OnPush` change detection, which is optimal for SSR:
- Fewer change detection cycles
- Better performance in both server and browser
- Compatible with zoneless applications
### Lazy Initialization
Browser-only features are lazily initialized:
- Event listeners attach only in browser
- Media queries only checked in browser
- Navigator API only accessed in browser
## Zone.js and Zoneless
The datepicker works with or without Zone.js:
- ✅ Works with Zone.js (default)
- ✅ Works without Zone.js (zoneless)
- Uses `ChangeDetectorRef.markForCheck()` for manual change detection
- Compatible with Angular's zoneless mode
## Example: Full SSR Setup
```typescript
import { Component, signal, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { httpResource } from '@angular/common/http';
import { linkedSignal, computed } from '@angular/core';
import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
@Component({
selector: 'app-ssr-example',
standalone: true,
imports: [NgxsmkDatepickerComponent],
template: `
<ngxsmk-datepicker
[value]="selectedDate()"
(valueChange)="onDateChange($event)"
mode="single"
placeholder="Select a date">
</ngxsmk-datepicker>
`
})
export class SSRExampleComponent {
private http = inject(HttpClient);
// Fetch data (works on both server and client)
resource = httpResource({
request: () => this.http.get<{ date: string }>('/api/data'),
loader: signal(false)
});
// Link to response
localObject = linkedSignal(() => this.resource.response.value());
// Compute date (SSR-safe)
selectedDate = computed(() => {
const obj = this.localObject();
return obj?.date ? new Date(obj.date) : null;
});
onDateChange(date: Date | null) {
// Update logic here
console.log('Date changed:', date);
}
}
```
## Troubleshooting Checklist
- [ ] Build completes without errors: `npm run build:ssr`
- [ ] Server starts without errors: `npm run serve:ssr`
- [ ] No `window is not defined` errors in server logs
- [ ] Datepicker renders in server HTML
- [ ] Datepicker works after client hydration
- [ ] No console errors in browser
- [ ] Dates display correctly
- [ ] Locale works as expected
## Additional Resources
- **[SSR Example](./SSR-EXAMPLE.md)** - Complete working example with code samples
- [Angular Universal Guide](https://angular.dev/guide/ssr)
- [Angular SSR Documentation](https://angular.dev/guide/ssr)
- [Platform Detection](https://angular.dev/api/common/isPlatformBrowser)