ngxsmk-datepicker 2.3.1 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,427 +1,427 @@
1
- # Server-Side Rendering (SSR) Example
2
-
3
- **Last updated:** June 3, 2026 - **Current stable:** v2.3.1
4
-
5
- Complete example demonstrating ngxsmk-datepicker with Angular Universal (SSR).
6
-
7
- ## Overview
8
-
9
- ngxsmk-datepicker is fully compatible with Angular Universal and server-side rendering. All browser APIs are properly guarded with platform checks, ensuring the component works seamlessly in both server and browser environments.
10
-
11
- > **Note**: For setup instructions and best practices, see the [SSR Guide](./ssr.md).
12
-
13
- ## Basic SSR Setup
14
-
15
- ### 1. Install Angular Universal
16
-
17
- ```bash
18
- ng add @nguniversal/express-engine
19
- ```
20
-
21
- ### 2. Configure App for SSR
22
-
23
- ```typescript
24
- // app.config.ts
25
- import { ApplicationConfig, PLATFORM_ID } from '@angular/core';
26
- import { provideRouter } from '@angular/router';
27
- import { isPlatformBrowser } from '@angular/common';
28
- import { provideDatepickerConfig } from 'ngxsmk-datepicker';
29
-
30
- export const appConfig: ApplicationConfig = {
31
- providers: [
32
- provideRouter(routes),
33
- provideDatepickerConfig({
34
- locale: 'en-US', // Explicitly set locale for SSR consistency
35
- weekStart: 1,
36
- minuteInterval: 15
37
- })
38
- ]
39
- };
40
- ```
41
-
42
- ### 3. Component Example
43
-
44
- ```typescript
45
- // datepicker-demo.component.ts
46
- import { Component, PLATFORM_ID, inject } from '@angular/core';
47
- import { isPlatformBrowser } from '@angular/common';
48
- import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
49
-
50
- @Component({
51
- selector: 'app-datepicker-demo',
52
- standalone: true,
53
- imports: [NgxsmkDatepickerComponent],
54
- template: `
55
- <div class="datepicker-container">
56
- <h2>Date Selection</h2>
57
- <ngxsmk-datepicker
58
- mode="single"
59
- [locale]="locale"
60
- placeholder="Select a date"
61
- (valueChange)="onDateChange($event)">
62
- </ngxsmk-datepicker>
63
-
64
- @if (selectedDate) {
65
- <p>Selected: {{ selectedDate | date:'fullDate' }}</p>
66
- }
67
- </div>
68
- `
69
- })
70
- export class DatepickerDemoComponent {
71
- private platformId = inject(PLATFORM_ID);
72
-
73
- selectedDate: Date | null = null;
74
-
75
- // Use explicit locale for SSR consistency
76
- locale = isPlatformBrowser(this.platformId)
77
- ? navigator.language || 'en-US'
78
- : 'en-US';
79
-
80
- onDateChange(date: Date | null): void {
81
- this.selectedDate = date;
82
- }
83
- }
84
- ```
85
-
86
- ## SSR-Specific Considerations
87
-
88
- ### 1. Locale Detection
89
-
90
- Always provide an explicit locale for SSR consistency:
91
-
92
- ```typescript
93
- // ❌ Bad - relies on browser API
94
- locale = navigator.language;
95
-
96
- // ✅ Good - platform-checked
97
- locale = isPlatformBrowser(this.platformId)
98
- ? navigator.language || 'en-US'
99
- : 'en-US';
100
-
101
- // ✅ Better - use service or config
102
- locale = this.localeService.getLocale(); // Returns 'en-US' on server
103
- ```
104
-
105
- ### 2. Date Initialization
106
-
107
- Dates work the same on server and client:
108
-
109
- ```typescript
110
- // ✅ Safe - Date works on both server and client
111
- const today = new Date();
112
- const minDate = new Date(2025, 0, 1); // January 1, 2025
113
- ```
114
-
115
- ### 3. Value Hydration
116
-
117
- The component handles value hydration automatically:
118
-
119
- ```typescript
120
- @Component({
121
- template: `
122
- <ngxsmk-datepicker
123
- [value]="serverDate"
124
- (valueChange)="onDateChange($event)">
125
- </ngxsmk-datepicker>
126
- `
127
- })
128
- export class MyComponent {
129
- // Value from server API
130
- serverDate: Date | null = null;
131
-
132
- ngOnInit(): void {
133
- // Fetch from API
134
- this.apiService.getDate().subscribe(date => {
135
- this.serverDate = new Date(date); // Works on both server and client
136
- });
137
- }
138
- }
139
- ```
140
-
141
- ## Complete SSR Example
142
-
143
- ### Server-Side Component
144
-
145
- ```typescript
146
- // server.component.ts
147
- import { Component, OnInit, PLATFORM_ID, inject } from '@angular/core';
148
- import { isPlatformBrowser } from '@angular/common';
149
- import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
150
- import { DateService } from './date.service';
151
-
152
- @Component({
153
- selector: 'app-server-demo',
154
- standalone: true,
155
- imports: [NgxsmkDatepickerComponent],
156
- template: `
157
- <div class="server-demo">
158
- <h1>SSR Datepicker Demo</h1>
159
-
160
- <section>
161
- <h2>Single Date Selection</h2>
162
- <ngxsmk-datepicker
163
- mode="single"
164
- [locale]="locale"
165
- [value]="initialDate"
166
- placeholder="Select a date"
167
- (valueChange)="onSingleDateChange($event)">
168
- </ngxsmk-datepicker>
169
- <p *ngIf="singleDate">Selected: {{ singleDate | date:'fullDate' }}</p>
170
- </section>
171
-
172
- <section>
173
- <h2>Date Range Selection</h2>
174
- <ngxsmk-datepicker
175
- mode="range"
176
- [locale]="locale"
177
- [value]="initialRange"
178
- placeholder="Select date range"
179
- (valueChange)="onRangeChange($event)">
180
- </ngxsmk-datepicker>
181
- <p *ngIf="dateRange">
182
- Range: {{ dateRange.start | date:'shortDate' }} -
183
- {{ dateRange.end | date:'shortDate' }}
184
- </p>
185
- </section>
186
- </div>
187
- `
188
- })
189
- export class ServerDemoComponent implements OnInit {
190
- private platformId = inject(PLATFORM_ID);
191
- private dateService = inject(DateService);
192
-
193
- locale = 'en-US';
194
- initialDate: Date | null = null;
195
- initialRange: { start: Date; end: Date } | null = null;
196
- singleDate: Date | null = null;
197
- dateRange: { start: Date; end: Date } | null = null;
198
-
199
- ngOnInit(): void {
200
- // Set locale based on platform
201
- if (isPlatformBrowser(this.platformId)) {
202
- this.locale = navigator.language || 'en-US';
203
- }
204
-
205
- // Load initial data (works on both server and client)
206
- this.loadInitialData();
207
- }
208
-
209
- private loadInitialData(): void {
210
- // Simulate server-side data loading
211
- this.dateService.getInitialDate().subscribe(date => {
212
- this.initialDate = date ? new Date(date) : null;
213
- });
214
-
215
- this.dateService.getInitialRange().subscribe(range => {
216
- if (range) {
217
- this.initialRange = {
218
- start: new Date(range.start),
219
- end: new Date(range.end)
220
- };
221
- }
222
- });
223
- }
224
-
225
- onSingleDateChange(date: Date | null): void {
226
- this.singleDate = date;
227
- // Save to server
228
- if (date) {
229
- this.dateService.saveDate(date).subscribe();
230
- }
231
- }
232
-
233
- onRangeChange(range: { start: Date; end: Date } | null): void {
234
- this.dateRange = range;
235
- // Save to server
236
- if (range) {
237
- this.dateService.saveRange(range).subscribe();
238
- }
239
- }
240
- }
241
- ```
242
-
243
- ### Service Example
244
-
245
- ```typescript
246
- // date.service.ts
247
- import { Injectable } from '@angular/core';
248
- import { HttpClient } from '@angular/common/http';
249
- import { Observable } from 'rxjs';
250
-
251
- @Injectable({
252
- providedIn: 'root'
253
- })
254
- export class DateService {
255
- constructor(private http: HttpClient) {}
256
-
257
- getInitialDate(): Observable<string | null> {
258
- // Works on both server and client
259
- return this.http.get<string | null>('/api/initial-date');
260
- }
261
-
262
- getInitialRange(): Observable<{ start: string; end: string } | null> {
263
- return this.http.get<{ start: string; end: string } | null>('/api/initial-range');
264
- }
265
-
266
- saveDate(date: Date): Observable<void> {
267
- return this.http.post<void>('/api/date', { date: date.toISOString() });
268
- }
269
-
270
- saveRange(range: { start: Date; end: Date }): Observable<void> {
271
- return this.http.post<void>('/api/range', {
272
- start: range.start.toISOString(),
273
- end: range.end.toISOString()
274
- });
275
- }
276
- }
277
- ```
278
-
279
- ## Hydration Notes
280
-
281
- ### Automatic Hydration
282
-
283
- The component automatically handles hydration when values are set:
284
-
285
- ```typescript
286
- // Value set on server
287
- component.value = new Date('2025-01-15');
288
-
289
- // Component automatically:
290
- // 1. Initializes the selected date
291
- // 2. Generates the calendar
292
- // 3. Updates the display value
293
- // 4. Works seamlessly after hydration
294
- ```
295
-
296
- ### Manual Hydration
297
-
298
- If you need to manually trigger hydration:
299
-
300
- ```typescript
301
- @Component({
302
- template: `
303
- <ngxsmk-datepicker
304
- #datepicker
305
- [value]="serverDate">
306
- </ngxsmk-datepicker>
307
- `
308
- })
309
- export class MyComponent implements AfterViewInit {
310
- @ViewChild('datepicker') datepicker!: NgxsmkDatepickerComponent;
311
- serverDate: Date | null = null;
312
-
313
- ngAfterViewInit(): void {
314
- // After view init, ensure component is hydrated
315
- if (this.serverDate) {
316
- // Component handles this automatically, but you can force update if needed
317
- this.datepicker.ngOnChanges({
318
- value: new SimpleChange(null, this.serverDate, true)
319
- });
320
- }
321
- }
322
- }
323
- ```
324
-
325
- ## Common SSR Issues & Solutions
326
-
327
- ### Issue 1: Locale Not Set
328
-
329
- **Problem**: Component uses browser locale on server, causing inconsistencies.
330
-
331
- **Solution**: Always provide explicit locale:
332
-
333
- ```typescript
334
- // ✅ Correct
335
- <ngxsmk-datepicker [locale]="'en-US'"></ngxsmk-datepicker>
336
-
337
- // Or use platform check
338
- locale = isPlatformBrowser(this.platformId)
339
- ? navigator.language
340
- : 'en-US';
341
- ```
342
-
343
- ### Issue 2: Date Formatting Differences
344
-
345
- **Problem**: Date formatting may differ between server and client.
346
-
347
- **Solution**: Use consistent locale and timezone:
348
-
349
- ```typescript
350
- // ✅ Correct
351
- provideDatepickerConfig({
352
- locale: 'en-US',
353
- timezone: 'UTC' // Use UTC for consistency
354
- })
355
- ```
356
-
357
- ### Issue 3: Window/Document Access
358
-
359
- **Problem**: Component tries to access window/document on server.
360
-
361
- **Solution**: Component already handles this with platform checks. No action needed.
362
-
363
- ## Testing SSR
364
-
365
- ### Unit Tests
366
-
367
- ```typescript
368
- // datepicker-ssr.spec.ts
369
- import { TestBed } from '@angular/core/testing';
370
- import { PLATFORM_ID } from '@angular/core';
371
- import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
372
-
373
- describe('SSR Compatibility', () => {
374
- it('should work on server platform', () => {
375
- TestBed.configureTestingModule({
376
- imports: [NgxsmkDatepickerComponent],
377
- providers: [
378
- { provide: PLATFORM_ID, useValue: 'server' }
379
- ]
380
- });
381
-
382
- const fixture = TestBed.createComponent(NgxsmkDatepickerComponent);
383
- expect(() => fixture.detectChanges()).not.toThrow();
384
- });
385
- });
386
- ```
387
-
388
- ### E2E Tests
389
-
390
- ```typescript
391
- // e2e/ssr.spec.ts
392
- import { test, expect } from '@playwright/test';
393
-
394
- test('datepicker works after SSR hydration', async ({ page }) => {
395
- await page.goto('/');
396
-
397
- // Wait for hydration
398
- await page.waitForSelector('ngxsmk-datepicker');
399
-
400
- // Click datepicker
401
- await page.click('.ngxsmk-input-group');
402
-
403
- // Select a date
404
- await page.click('.ngxsmk-day-cell:has-text("15")');
405
-
406
- // Verify value is set
407
- const value = await page.inputValue('.ngxsmk-display-input');
408
- expect(value).toBeTruthy();
409
- });
410
- ```
411
-
412
- ## Best Practices
413
-
414
- 1. **Always Set Locale**: Provide explicit locale for SSR consistency
415
- 2. **Use Platform Checks**: Check `isPlatformBrowser` for browser-only code
416
- 3. **Test Both Platforms**: Test on both server and client
417
- 4. **Use UTC for Consistency**: Consider using UTC timezone for server/client consistency
418
- 5. **Handle Hydration**: Let the component handle hydration automatically
419
-
420
- ## Additional Resources
421
-
422
- - [Angular Universal Guide](https://angular.io/guide/universal)
423
- - [SSR Best Practices](https://angular.io/guide/universal#best-practices)
424
- - [Platform Detection](https://angular.io/api/common/isPlatformBrowser)
425
-
426
-
427
-
1
+ # Server-Side Rendering (SSR) Example
2
+
3
+ **Last updated:** July 2, 2026 - **Current stable:** v2.4.0
4
+
5
+ Complete example demonstrating ngxsmk-datepicker with Angular Universal (SSR).
6
+
7
+ ## Overview
8
+
9
+ ngxsmk-datepicker is fully compatible with Angular Universal and server-side rendering. All browser APIs are properly guarded with platform checks, ensuring the component works seamlessly in both server and browser environments.
10
+
11
+ > **Note**: For setup instructions and best practices, see the [SSR Guide](./ssr.md).
12
+
13
+ ## Basic SSR Setup
14
+
15
+ ### 1. Install Angular Universal
16
+
17
+ ```bash
18
+ ng add @nguniversal/express-engine
19
+ ```
20
+
21
+ ### 2. Configure App for SSR
22
+
23
+ ```typescript
24
+ // app.config.ts
25
+ import { ApplicationConfig, PLATFORM_ID } from '@angular/core';
26
+ import { provideRouter } from '@angular/router';
27
+ import { isPlatformBrowser } from '@angular/common';
28
+ import { provideDatepickerConfig } from 'ngxsmk-datepicker';
29
+
30
+ export const appConfig: ApplicationConfig = {
31
+ providers: [
32
+ provideRouter(routes),
33
+ provideDatepickerConfig({
34
+ locale: 'en-US', // Explicitly set locale for SSR consistency
35
+ weekStart: 1,
36
+ minuteInterval: 15
37
+ })
38
+ ]
39
+ };
40
+ ```
41
+
42
+ ### 3. Component Example
43
+
44
+ ```typescript
45
+ // datepicker-demo.component.ts
46
+ import { Component, PLATFORM_ID, inject } from '@angular/core';
47
+ import { isPlatformBrowser } from '@angular/common';
48
+ import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
49
+
50
+ @Component({
51
+ selector: 'app-datepicker-demo',
52
+ standalone: true,
53
+ imports: [NgxsmkDatepickerComponent],
54
+ template: `
55
+ <div class="datepicker-container">
56
+ <h2>Date Selection</h2>
57
+ <ngxsmk-datepicker
58
+ mode="single"
59
+ [locale]="locale"
60
+ placeholder="Select a date"
61
+ (valueChange)="onDateChange($event)">
62
+ </ngxsmk-datepicker>
63
+
64
+ @if (selectedDate) {
65
+ <p>Selected: {{ selectedDate | date:'fullDate' }}</p>
66
+ }
67
+ </div>
68
+ `
69
+ })
70
+ export class DatepickerDemoComponent {
71
+ private platformId = inject(PLATFORM_ID);
72
+
73
+ selectedDate: Date | null = null;
74
+
75
+ // Use explicit locale for SSR consistency
76
+ locale = isPlatformBrowser(this.platformId)
77
+ ? navigator.language || 'en-US'
78
+ : 'en-US';
79
+
80
+ onDateChange(date: Date | null): void {
81
+ this.selectedDate = date;
82
+ }
83
+ }
84
+ ```
85
+
86
+ ## SSR-Specific Considerations
87
+
88
+ ### 1. Locale Detection
89
+
90
+ Always provide an explicit locale for SSR consistency:
91
+
92
+ ```typescript
93
+ // ❌ Bad - relies on browser API
94
+ locale = navigator.language;
95
+
96
+ // ✅ Good - platform-checked
97
+ locale = isPlatformBrowser(this.platformId)
98
+ ? navigator.language || 'en-US'
99
+ : 'en-US';
100
+
101
+ // ✅ Better - use service or config
102
+ locale = this.localeService.getLocale(); // Returns 'en-US' on server
103
+ ```
104
+
105
+ ### 2. Date Initialization
106
+
107
+ Dates work the same on server and client:
108
+
109
+ ```typescript
110
+ // ✅ Safe - Date works on both server and client
111
+ const today = new Date();
112
+ const minDate = new Date(2025, 0, 1); // January 1, 2025
113
+ ```
114
+
115
+ ### 3. Value Hydration
116
+
117
+ The component handles value hydration automatically:
118
+
119
+ ```typescript
120
+ @Component({
121
+ template: `
122
+ <ngxsmk-datepicker
123
+ [value]="serverDate"
124
+ (valueChange)="onDateChange($event)">
125
+ </ngxsmk-datepicker>
126
+ `
127
+ })
128
+ export class MyComponent {
129
+ // Value from server API
130
+ serverDate: Date | null = null;
131
+
132
+ ngOnInit(): void {
133
+ // Fetch from API
134
+ this.apiService.getDate().subscribe(date => {
135
+ this.serverDate = new Date(date); // Works on both server and client
136
+ });
137
+ }
138
+ }
139
+ ```
140
+
141
+ ## Complete SSR Example
142
+
143
+ ### Server-Side Component
144
+
145
+ ```typescript
146
+ // server.component.ts
147
+ import { Component, OnInit, PLATFORM_ID, inject } from '@angular/core';
148
+ import { isPlatformBrowser } from '@angular/common';
149
+ import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
150
+ import { DateService } from './date.service';
151
+
152
+ @Component({
153
+ selector: 'app-server-demo',
154
+ standalone: true,
155
+ imports: [NgxsmkDatepickerComponent],
156
+ template: `
157
+ <div class="server-demo">
158
+ <h1>SSR Datepicker Demo</h1>
159
+
160
+ <section>
161
+ <h2>Single Date Selection</h2>
162
+ <ngxsmk-datepicker
163
+ mode="single"
164
+ [locale]="locale"
165
+ [value]="initialDate"
166
+ placeholder="Select a date"
167
+ (valueChange)="onSingleDateChange($event)">
168
+ </ngxsmk-datepicker>
169
+ <p *ngIf="singleDate">Selected: {{ singleDate | date:'fullDate' }}</p>
170
+ </section>
171
+
172
+ <section>
173
+ <h2>Date Range Selection</h2>
174
+ <ngxsmk-datepicker
175
+ mode="range"
176
+ [locale]="locale"
177
+ [value]="initialRange"
178
+ placeholder="Select date range"
179
+ (valueChange)="onRangeChange($event)">
180
+ </ngxsmk-datepicker>
181
+ <p *ngIf="dateRange">
182
+ Range: {{ dateRange.start | date:'shortDate' }} -
183
+ {{ dateRange.end | date:'shortDate' }}
184
+ </p>
185
+ </section>
186
+ </div>
187
+ `
188
+ })
189
+ export class ServerDemoComponent implements OnInit {
190
+ private platformId = inject(PLATFORM_ID);
191
+ private dateService = inject(DateService);
192
+
193
+ locale = 'en-US';
194
+ initialDate: Date | null = null;
195
+ initialRange: { start: Date; end: Date } | null = null;
196
+ singleDate: Date | null = null;
197
+ dateRange: { start: Date; end: Date } | null = null;
198
+
199
+ ngOnInit(): void {
200
+ // Set locale based on platform
201
+ if (isPlatformBrowser(this.platformId)) {
202
+ this.locale = navigator.language || 'en-US';
203
+ }
204
+
205
+ // Load initial data (works on both server and client)
206
+ this.loadInitialData();
207
+ }
208
+
209
+ private loadInitialData(): void {
210
+ // Simulate server-side data loading
211
+ this.dateService.getInitialDate().subscribe(date => {
212
+ this.initialDate = date ? new Date(date) : null;
213
+ });
214
+
215
+ this.dateService.getInitialRange().subscribe(range => {
216
+ if (range) {
217
+ this.initialRange = {
218
+ start: new Date(range.start),
219
+ end: new Date(range.end)
220
+ };
221
+ }
222
+ });
223
+ }
224
+
225
+ onSingleDateChange(date: Date | null): void {
226
+ this.singleDate = date;
227
+ // Save to server
228
+ if (date) {
229
+ this.dateService.saveDate(date).subscribe();
230
+ }
231
+ }
232
+
233
+ onRangeChange(range: { start: Date; end: Date } | null): void {
234
+ this.dateRange = range;
235
+ // Save to server
236
+ if (range) {
237
+ this.dateService.saveRange(range).subscribe();
238
+ }
239
+ }
240
+ }
241
+ ```
242
+
243
+ ### Service Example
244
+
245
+ ```typescript
246
+ // date.service.ts
247
+ import { Injectable } from '@angular/core';
248
+ import { HttpClient } from '@angular/common/http';
249
+ import { Observable } from 'rxjs';
250
+
251
+ @Injectable({
252
+ providedIn: 'root'
253
+ })
254
+ export class DateService {
255
+ constructor(private http: HttpClient) {}
256
+
257
+ getInitialDate(): Observable<string | null> {
258
+ // Works on both server and client
259
+ return this.http.get<string | null>('/api/initial-date');
260
+ }
261
+
262
+ getInitialRange(): Observable<{ start: string; end: string } | null> {
263
+ return this.http.get<{ start: string; end: string } | null>('/api/initial-range');
264
+ }
265
+
266
+ saveDate(date: Date): Observable<void> {
267
+ return this.http.post<void>('/api/date', { date: date.toISOString() });
268
+ }
269
+
270
+ saveRange(range: { start: Date; end: Date }): Observable<void> {
271
+ return this.http.post<void>('/api/range', {
272
+ start: range.start.toISOString(),
273
+ end: range.end.toISOString()
274
+ });
275
+ }
276
+ }
277
+ ```
278
+
279
+ ## Hydration Notes
280
+
281
+ ### Automatic Hydration
282
+
283
+ The component automatically handles hydration when values are set:
284
+
285
+ ```typescript
286
+ // Value set on server
287
+ component.value = new Date('2025-01-15');
288
+
289
+ // Component automatically:
290
+ // 1. Initializes the selected date
291
+ // 2. Generates the calendar
292
+ // 3. Updates the display value
293
+ // 4. Works seamlessly after hydration
294
+ ```
295
+
296
+ ### Manual Hydration
297
+
298
+ If you need to manually trigger hydration:
299
+
300
+ ```typescript
301
+ @Component({
302
+ template: `
303
+ <ngxsmk-datepicker
304
+ #datepicker
305
+ [value]="serverDate">
306
+ </ngxsmk-datepicker>
307
+ `
308
+ })
309
+ export class MyComponent implements AfterViewInit {
310
+ @ViewChild('datepicker') datepicker!: NgxsmkDatepickerComponent;
311
+ serverDate: Date | null = null;
312
+
313
+ ngAfterViewInit(): void {
314
+ // After view init, ensure component is hydrated
315
+ if (this.serverDate) {
316
+ // Component handles this automatically, but you can force update if needed
317
+ this.datepicker.ngOnChanges({
318
+ value: new SimpleChange(null, this.serverDate, true)
319
+ });
320
+ }
321
+ }
322
+ }
323
+ ```
324
+
325
+ ## Common SSR Issues & Solutions
326
+
327
+ ### Issue 1: Locale Not Set
328
+
329
+ **Problem**: Component uses browser locale on server, causing inconsistencies.
330
+
331
+ **Solution**: Always provide explicit locale:
332
+
333
+ ```typescript
334
+ // ✅ Correct
335
+ <ngxsmk-datepicker [locale]="'en-US'"></ngxsmk-datepicker>
336
+
337
+ // Or use platform check
338
+ locale = isPlatformBrowser(this.platformId)
339
+ ? navigator.language
340
+ : 'en-US';
341
+ ```
342
+
343
+ ### Issue 2: Date Formatting Differences
344
+
345
+ **Problem**: Date formatting may differ between server and client.
346
+
347
+ **Solution**: Use consistent locale and timezone:
348
+
349
+ ```typescript
350
+ // ✅ Correct
351
+ provideDatepickerConfig({
352
+ locale: 'en-US',
353
+ timezone: 'UTC' // Use UTC for consistency
354
+ })
355
+ ```
356
+
357
+ ### Issue 3: Window/Document Access
358
+
359
+ **Problem**: Component tries to access window/document on server.
360
+
361
+ **Solution**: Component already handles this with platform checks. No action needed.
362
+
363
+ ## Testing SSR
364
+
365
+ ### Unit Tests
366
+
367
+ ```typescript
368
+ // datepicker-ssr.spec.ts
369
+ import { TestBed } from '@angular/core/testing';
370
+ import { PLATFORM_ID } from '@angular/core';
371
+ import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
372
+
373
+ describe('SSR Compatibility', () => {
374
+ it('should work on server platform', () => {
375
+ TestBed.configureTestingModule({
376
+ imports: [NgxsmkDatepickerComponent],
377
+ providers: [
378
+ { provide: PLATFORM_ID, useValue: 'server' }
379
+ ]
380
+ });
381
+
382
+ const fixture = TestBed.createComponent(NgxsmkDatepickerComponent);
383
+ expect(() => fixture.detectChanges()).not.toThrow();
384
+ });
385
+ });
386
+ ```
387
+
388
+ ### E2E Tests
389
+
390
+ ```typescript
391
+ // e2e/ssr.spec.ts
392
+ import { test, expect } from '@playwright/test';
393
+
394
+ test('datepicker works after SSR hydration', async ({ page }) => {
395
+ await page.goto('/');
396
+
397
+ // Wait for hydration
398
+ await page.waitForSelector('ngxsmk-datepicker');
399
+
400
+ // Click datepicker
401
+ await page.click('.ngxsmk-input-group');
402
+
403
+ // Select a date
404
+ await page.click('.ngxsmk-day-cell:has-text("15")');
405
+
406
+ // Verify value is set
407
+ const value = await page.inputValue('.ngxsmk-display-input');
408
+ expect(value).toBeTruthy();
409
+ });
410
+ ```
411
+
412
+ ## Best Practices
413
+
414
+ 1. **Always Set Locale**: Provide explicit locale for SSR consistency
415
+ 2. **Use Platform Checks**: Check `isPlatformBrowser` for browser-only code
416
+ 3. **Test Both Platforms**: Test on both server and client
417
+ 4. **Use UTC for Consistency**: Consider using UTC timezone for server/client consistency
418
+ 5. **Handle Hydration**: Let the component handle hydration automatically
419
+
420
+ ## Additional Resources
421
+
422
+ - [Angular Universal Guide](https://angular.io/guide/universal)
423
+ - [SSR Best Practices](https://angular.io/guide/universal#best-practices)
424
+ - [Platform Detection](https://angular.io/api/common/isPlatformBrowser)
425
+
426
+
427
+