flagmint-react-sdk 0.7.10 → 0.7.12

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.
Files changed (2) hide show
  1. package/README.md +609 -0
  2. package/package.json +2 -2
package/README.md ADDED
@@ -0,0 +1,609 @@
1
+ # flagmint-react-sdk
2
+
3
+ A React wrapper for [flagmint-js-sdk](https://www.npmjs.com/package/flagmint-js-sdk) that provides React-specific hooks and context with fine-grained reactivity powered by Zustand.
4
+
5
+ ## ✨ Features
6
+
7
+ - 🎯 **Fine-grained reactivity** - Only components using specific flags re-render when those flags change
8
+ - 🚀 **SSR-safe** - No global state leakage between server requests
9
+ - 📦 **TypeScript support** - Full type safety with generics for flag values and context
10
+ - 🔀 **Dual export strategy** - Separate server-safe types and client-only hooks
11
+ - ⚡ **Auto-initialization** - Direct integration with FlagClient from flagmint-js-sdk
12
+ - 🔐 **Deferred initialization** - Perfect for authentication flows
13
+ - 📱 **Framework agnostic** - Works with Next.js, Remix, Vite, and more
14
+
15
+ ## 📦 Installation
16
+
17
+ ```bash
18
+ npm install react-flagmint flagmint-js-sdk
19
+ # or
20
+ pnpm add react-flagmint flagmint-js-sdk
21
+ # or
22
+ yarn add react-flagmint flagmint-js-sdk
23
+ ```
24
+
25
+ ## 🚀 Quick Start
26
+
27
+ ### 1. Basic Setup
28
+
29
+ ```tsx
30
+ // app/providers.tsx (Next.js App Router)
31
+ 'use client'
32
+ import { FlagmintProvider } from 'react-flagmint/client'
33
+
34
+ export default function Providers({ children }: { children: React.ReactNode }) {
35
+ return (
36
+ <FlagmintProvider
37
+ options={{
38
+ apiKey: process.env.NEXT_PUBLIC_FLAGMINT_API_KEY!,
39
+ transportMode: 'websocket',
40
+ context: {
41
+ userId: 'user-123',
42
+ plan: 'premium'
43
+ }
44
+ }}
45
+ >
46
+ {children}
47
+ </FlagmintProvider>
48
+ )
49
+ }
50
+ ```
51
+
52
+ ```tsx
53
+ // app/layout.tsx
54
+ import Providers from './providers'
55
+
56
+ export default function RootLayout({
57
+ children,
58
+ }: {
59
+ children: React.ReactNode
60
+ }) {
61
+ return (
62
+ <html>
63
+ <body>
64
+ <Providers>
65
+ {children}
66
+ </Providers>
67
+ </body>
68
+ </html>
69
+ )
70
+ }
71
+ ```
72
+
73
+ ### 2. Using Flags in Components
74
+
75
+ ```tsx
76
+ // components/FeatureComponent.tsx
77
+ 'use client'
78
+ import { useFlag, useFlags, useFlagmintReady } from 'react-flagmint/client'
79
+
80
+ export default function FeatureComponent() {
81
+ const showNewFeature = useFlag('new-dashboard', false)
82
+ const userPlan = useFlag('user-plan', 'free')
83
+ const isReady = useFlagmintReady()
84
+
85
+ if (!isReady) {
86
+ return <div>Loading flags...</div>
87
+ }
88
+
89
+ return (
90
+ <div>
91
+ {showNewFeature && <NewDashboard />}
92
+ <div>Current plan: {userPlan}</div>
93
+ </div>
94
+ )
95
+ }
96
+ ```
97
+
98
+ ## 📚 API Reference
99
+
100
+ ### FlagmintProvider
101
+
102
+ The provider component that initializes the FlagClient and provides flag context to your app.
103
+
104
+ ```tsx
105
+ <FlagmintProvider
106
+ options={FlagClientOptions}
107
+ initialFlags={{}}
108
+ deferInitialization={false}
109
+ >
110
+ {children}
111
+ </FlagmintProvider>
112
+ ```
113
+
114
+ #### Props
115
+
116
+ | Prop | Type | Default | Description |
117
+ |------|------|---------|-------------|
118
+ | `options` | `FlagClientOptions` | Required | Configuration for the FlagClient |
119
+ | `initialFlags` | `Flags` | `{}` | Initial flag values for SSR/hydration |
120
+ | `deferInitialization` | `boolean` | `false` | If true, wait for manual initialization |
121
+
122
+ #### FlagClientOptions
123
+
124
+ ```tsx
125
+ interface FlagClientOptions<C extends Record<string, any> = Record<string, any>> {
126
+ apiKey: string
127
+ context?: C
128
+ transportMode?: 'auto' | 'websocket' | 'long-polling'
129
+ enableOfflineCache?: boolean
130
+ persistContext?: boolean
131
+ onError?: (error: Error) => void
132
+ previewMode?: boolean
133
+ rawFlags?: Record<string, FlagValue>
134
+ deferInitialization?: boolean
135
+ }
136
+ ```
137
+
138
+ ### Hooks
139
+
140
+ #### useFlag(key, fallback?)
141
+
142
+ Get a specific flag value with fine-grained reactivity.
143
+
144
+ ```tsx
145
+ const showFeature = useFlag('feature-name', false)
146
+ const userTier = useFlag('user-tier', 'free')
147
+ const config = useFlag('app-config', { theme: 'light' })
148
+ ```
149
+
150
+ **Parameters:**
151
+ - `key: string` - The flag key
152
+ - `fallback?: T` - Default value if flag is not found
153
+
154
+ **Returns:** The flag value or fallback
155
+
156
+ #### useFlags()
157
+
158
+ Get all currently loaded flags.
159
+
160
+ ```tsx
161
+ const allFlags = useFlags()
162
+ console.log(allFlags) // { 'feature-a': true, 'user-tier': 'pro' }
163
+ ```
164
+
165
+ **Returns:** Object containing all loaded flags
166
+
167
+ #### useFlagmint()
168
+
169
+ Get access to the FlagClient instance and utilities.
170
+
171
+ ```tsx
172
+ const { client, isReady, isInitialized, updateContext } = useFlagmint()
173
+
174
+ // Update user context
175
+ await updateContext({ userId: 'new-user', plan: 'enterprise' })
176
+ ```
177
+
178
+ **Returns:**
179
+ ```tsx
180
+ {
181
+ client: FlagClient | null
182
+ isReady: boolean
183
+ isInitialized: boolean
184
+ updateContext: (context: Record<string, any>) => Promise<void>
185
+ }
186
+ ```
187
+
188
+ #### useFlagmintReady()
189
+
190
+ Check if the FlagClient is ready to serve flags.
191
+
192
+ ```tsx
193
+ const isReady = useFlagmintReady()
194
+
195
+ if (!isReady) {
196
+ return <LoadingSpinner />
197
+ }
198
+ ```
199
+
200
+ #### useFlagClient()
201
+
202
+ Get direct access to the FlagClient instance.
203
+
204
+ ```tsx
205
+ const client = useFlagClient<MyFlagTypes>()
206
+ const specificFlag = client?.getFlag('feature-x', false)
207
+ ```
208
+
209
+ ## 🔧 Usage Patterns
210
+
211
+ ### 1. Auto-initialization (Default)
212
+
213
+ Best when user context is available immediately:
214
+
215
+ ```tsx
216
+ function App() {
217
+ return (
218
+ <FlagmintProvider
219
+ options={{
220
+ apiKey: 'your-api-key',
221
+ context: {
222
+ userId: getCurrentUser().id,
223
+ plan: getCurrentUser().plan
224
+ }
225
+ }}
226
+ >
227
+ <Dashboard />
228
+ </FlagmintProvider>
229
+ )
230
+ }
231
+ ```
232
+
233
+ ### 2. Deferred Initialization
234
+
235
+ Perfect for authentication flows:
236
+
237
+ ```tsx
238
+ // Provider with deferred initialization
239
+ <FlagmintProvider
240
+ options={{
241
+ apiKey: 'your-api-key',
242
+ context: {} // Empty initially
243
+ }}
244
+ deferInitialization={true}
245
+ >
246
+ <App />
247
+ </FlagmintProvider>
248
+
249
+ // Login component
250
+ function LoginPage() {
251
+ const { updateContext } = useFlagmint()
252
+
253
+ const handleLogin = async (user) => {
254
+ // First update context with user info
255
+ await updateContext({
256
+ userId: user.id,
257
+ plan: user.plan,
258
+ locale: user.locale
259
+ })
260
+
261
+ // Flags are now available with user context
262
+ navigate('/dashboard')
263
+ }
264
+
265
+ return <LoginForm onLogin={handleLogin} />
266
+ }
267
+ ```
268
+
269
+ ### 3. TypeScript Usage
270
+
271
+ Define your flag types for better type safety:
272
+
273
+ ```tsx
274
+ // types/flags.ts
275
+ export interface AppFlags {
276
+ 'show-beta-feature': boolean
277
+ 'user-plan': 'free' | 'pro' | 'enterprise'
278
+ 'theme-config': {
279
+ primaryColor: string
280
+ darkMode: boolean
281
+ }
282
+ }
283
+
284
+ export interface UserContext {
285
+ userId: string
286
+ plan: string
287
+ locale: string
288
+ }
289
+
290
+ // components/TypedComponent.tsx
291
+ import { useFlag } from 'react-flagmint/client'
292
+ import type { AppFlags } from '../types/flags'
293
+
294
+ export default function TypedComponent() {
295
+ // TypeScript knows this returns boolean
296
+ const showBeta = useFlag<AppFlags['show-beta-feature']>('show-beta-feature', false)
297
+
298
+ // TypeScript knows this returns the union type
299
+ const plan = useFlag<AppFlags['user-plan']>('user-plan', 'free')
300
+
301
+ return (
302
+ <div>
303
+ {showBeta && <BetaFeature />}
304
+ <div>Plan: {plan}</div>
305
+ </div>
306
+ )
307
+ }
308
+ ```
309
+
310
+ ### 4. Server-Side Rendering (SSR)
311
+
312
+ #### Next.js App Router
313
+
314
+ ```tsx
315
+ // app/providers.tsx
316
+ 'use client'
317
+ import { FlagmintProvider } from 'react-flagmint/client'
318
+
319
+ export default function Providers({ children, initialFlags = {} }) {
320
+ return (
321
+ <FlagmintProvider
322
+ options={{
323
+ apiKey: process.env.NEXT_PUBLIC_FLAGMINT_API_KEY!,
324
+ transportMode: 'websocket'
325
+ }}
326
+ initialFlags={initialFlags}
327
+ >
328
+ {children}
329
+ </FlagmintProvider>
330
+ )
331
+ }
332
+
333
+ // app/page.tsx
334
+ import { FlagClient } from 'flagmint-js-sdk'
335
+ import Providers from './providers'
336
+ import ClientComponent from './ClientComponent'
337
+
338
+ export default async function Page() {
339
+ // Optionally preload flags on server
340
+ let initialFlags = {}
341
+
342
+ try {
343
+ const serverClient = new FlagClient({
344
+ apiKey: process.env.FLAGMINT_API_KEY!,
345
+ context: { server: true }
346
+ })
347
+ await serverClient.ready()
348
+ initialFlags = await serverClient.getFlags() // If this method exists
349
+ } catch (error) {
350
+ console.warn('Failed to load initial flags:', error)
351
+ }
352
+
353
+ return (
354
+ <Providers initialFlags={initialFlags}>
355
+ <ClientComponent />
356
+ </Providers>
357
+ )
358
+ }
359
+ ```
360
+
361
+ ### 5. Context Updates
362
+
363
+ Update user context dynamically:
364
+
365
+ ```tsx
366
+ function UserSettings() {
367
+ const { updateContext } = useFlagmint()
368
+ const currentTheme = useFlag('user-theme', 'light')
369
+
370
+ const handleThemeChange = async (newTheme: string) => {
371
+ // Update context - flags will be re-evaluated
372
+ await updateContext({ theme: newTheme })
373
+ }
374
+
375
+ const handlePlanUpgrade = async (newPlan: string) => {
376
+ await updateContext({ plan: newPlan })
377
+ // Component will re-render with new plan-based flags
378
+ }
379
+
380
+ return (
381
+ <div>
382
+ <ThemeSelector onChange={handleThemeChange} />
383
+ <PlanUpgrade onUpgrade={handlePlanUpgrade} />
384
+ </div>
385
+ )
386
+ }
387
+ ```
388
+
389
+ ### 6. Loading States
390
+
391
+ Handle loading states gracefully:
392
+
393
+ ```tsx
394
+ function FeaturePage() {
395
+ const isReady = useFlagmintReady()
396
+ const showPremiumFeature = useFlag('premium-feature', false)
397
+
398
+ if (!isReady) {
399
+ return (
400
+ <div className="loading-container">
401
+ <Spinner />
402
+ <p>Loading personalized features...</p>
403
+ </div>
404
+ )
405
+ }
406
+
407
+ return (
408
+ <div>
409
+ <h1>Features</h1>
410
+ {showPremiumFeature ? (
411
+ <PremiumFeature />
412
+ ) : (
413
+ <UpgradeBanner />
414
+ )}
415
+ </div>
416
+ )
417
+ }
418
+ ```
419
+
420
+ ### 7. Error Handling
421
+
422
+ ```tsx
423
+ <FlagmintProvider
424
+ options={{
425
+ apiKey: 'your-api-key',
426
+ onError: (error) => {
427
+ console.error('Flagmint error:', error)
428
+ // Send to error reporting service
429
+ errorReporting.captureException(error)
430
+ }
431
+ }}
432
+ >
433
+ <App />
434
+ </FlagmintProvider>
435
+ ```
436
+
437
+ ## 🏗️ Framework Examples
438
+
439
+ ### Next.js (App Router)
440
+
441
+ ```tsx
442
+ // app/layout.tsx
443
+ import Providers from './providers'
444
+
445
+ export default function RootLayout({ children }) {
446
+ return (
447
+ <html lang="en">
448
+ <body>
449
+ <Providers>{children}</Providers>
450
+ </body>
451
+ </html>
452
+ )
453
+ }
454
+
455
+ // app/providers.tsx
456
+ 'use client'
457
+ import { FlagmintProvider } from 'react-flagmint/client'
458
+
459
+ export default function Providers({ children }) {
460
+ return (
461
+ <FlagmintProvider
462
+ options={{
463
+ apiKey: process.env.NEXT_PUBLIC_FLAGMINT_API_KEY!,
464
+ transportMode: 'long-polling'
465
+ }}
466
+ >
467
+ {children}
468
+ </FlagmintProvider>
469
+ )
470
+ }
471
+ ```
472
+
473
+ ### Vite + React
474
+
475
+ ```tsx
476
+ // src/main.tsx
477
+ import React from 'react'
478
+ import ReactDOM from 'react-dom/client'
479
+ import { FlagmintProvider } from 'react-flagmint/client'
480
+ import App from './App'
481
+
482
+ ReactDOM.createRoot(document.getElementById('root')!).render(
483
+ <React.StrictMode>
484
+ <FlagmintProvider
485
+ options={{
486
+ apiKey: import.meta.env.VITE_FLAGMINT_API_KEY,
487
+ transportMode: 'websocket'
488
+ }}
489
+ >
490
+ <App />
491
+ </FlagmintProvider>
492
+ </React.StrictMode>
493
+ )
494
+ ```
495
+
496
+ ### Remix
497
+
498
+ ```tsx
499
+ // app/root.tsx
500
+ import { FlagmintProvider } from 'react-flagmint/client'
501
+
502
+ export default function App() {
503
+ return (
504
+ <html>
505
+ <head>
506
+ <Meta />
507
+ <Links />
508
+ </head>
509
+ <body>
510
+ <FlagmintProvider
511
+ options={{
512
+ apiKey: process.env.FLAGMINT_API_KEY!,
513
+ transportMode: 'long-polling'
514
+ }}
515
+ >
516
+ <Outlet />
517
+ </FlagmintProvider>
518
+ <Scripts />
519
+ </body>
520
+ </html>
521
+ )
522
+ }
523
+ ```
524
+
525
+ ## ⚡ Performance
526
+
527
+ ### Fine-grained Reactivity
528
+
529
+ Only components that use specific flags will re-render when those flags change:
530
+
531
+ ```tsx
532
+ // ✅ Good: Only re-renders when 'feature-a' changes
533
+ function ComponentA() {
534
+ const featureA = useFlag('feature-a', false)
535
+ return <div>{featureA && <FeatureA />}</div>
536
+ }
537
+
538
+ // ✅ Good: Only re-renders when 'feature-b' changes
539
+ function ComponentB() {
540
+ const featureB = useFlag('feature-b', false)
541
+ return <div>{featureB && <FeatureB />}</div>
542
+ }
543
+
544
+ // ❌ Avoid: Re-renders when ANY flag changes
545
+ function ComponentAll() {
546
+ const allFlags = useFlags()
547
+ return <div>{allFlags['feature-a'] && <FeatureA />}</div>
548
+ }
549
+ ```
550
+
551
+ ### Bundle Size
552
+
553
+ - **react-flagmint**: ~3KB gzipped
554
+ - **zustand**: ~2KB gzipped
555
+ - **Total overhead**: ~5KB gzipped
556
+
557
+ ## 🔧 Development
558
+
559
+ ### Local Development
560
+
561
+ ```bash
562
+ # Clone the monorepo
563
+ git clone <repo-url>
564
+ cd flagmint-monorepo
565
+
566
+ # Install dependencies
567
+ pnpm install
568
+
569
+ # Build the library
570
+ pnpm --filter react-flagmint build
571
+
572
+ # Run the Next.js example
573
+ pnpm dev:next
574
+ ```
575
+
576
+ ### Testing
577
+
578
+ ```bash
579
+ # Run tests
580
+ pnpm test
581
+
582
+ # Type checking
583
+ pnpm typecheck
584
+
585
+ # Lint
586
+ pnpm lint
587
+ ```
588
+
589
+ ## 📄 License
590
+
591
+ MIT
592
+
593
+ ## 🤝 Contributing
594
+
595
+ 1. Fork the repository
596
+ 2. Create a feature branch
597
+ 3. Make your changes
598
+ 4. Add tests
599
+ 5. Submit a pull request
600
+
601
+ ## 📞 Support
602
+
603
+ - 📧 Email: support@flagmint.com
604
+ - 🐛 Issues: [GitHub Issues](https://github.com/jtad009/flagmint-react-sdk/issues)
605
+ - 📖 Docs: [Documentation](https://docs.flagmint.com)
606
+
607
+ ---
608
+
609
+ **Made with ❤️ by the Flagmint team**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flagmint-react-sdk",
3
- "version": "0.7.10",
3
+ "version": "0.7.12",
4
4
  "description": "React wrapper for flagmint-js-sdk",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -31,7 +31,7 @@
31
31
  "react-dom": ">=18.0.0"
32
32
  },
33
33
  "dependencies": {
34
- "flagmint-js-sdk": "^1.2.3",
34
+ "flagmint-js-sdk": "^1.2.5",
35
35
  "zustand": "^4.4.0"
36
36
  },
37
37
  "devDependencies": {