Skip to content

Commit 1a3137c

Browse files
committed
feat: add next sdk
1 parent 8506492 commit 1a3137c

100 files changed

Lines changed: 9444 additions & 11 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

examples/README.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Examples
2+
3+
This directory contains example projects demonstrating how to use the Replane JavaScript SDKs.
4+
5+
Each example is an **independent project** that uses packages from npm (not workspace dependencies). This allows you to copy any example as a starting point for your own project.
6+
7+
## Available Examples
8+
9+
### [sdk](./sdk)
10+
11+
A Node.js example using `@replanejs/sdk` directly. Demonstrates:
12+
- Type-safe config access
13+
- Real-time subscription to config changes
14+
- Context-based override evaluation
15+
16+
### [react](./react)
17+
18+
A React + Vite example using `@replanejs/react`. Demonstrates:
19+
- `ReplaneProvider` with Suspense
20+
- `useConfig` hook for reactive config values
21+
- Fallback values for offline scenarios
22+
23+
### [next](./next)
24+
25+
A Next.js App Router example using `@replanejs/next`. Demonstrates:
26+
- Server-side config fetching with `getReplaneSnapshot()`
27+
- Zero-loading-state hydration
28+
- Real-time updates after SSR
29+
30+
## Running an Example
31+
32+
1. Navigate to the example directory:
33+
```bash
34+
cd examples/sdk # or react, next
35+
```
36+
37+
2. Install dependencies:
38+
```bash
39+
npm install
40+
```
41+
42+
3. Set up environment variables (see each example's README)
43+
44+
4. Run the example:
45+
```bash
46+
npm start # sdk
47+
npm run dev # react, next
48+
```

examples/next/.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules/
2+
.next/
3+
out/
4+
.env
5+
.env.local

examples/next/README.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# @replanejs/next Example
2+
3+
A Next.js App Router example demonstrating how to use `@replanejs/next` for server-side rendered feature flags with real-time updates.
4+
5+
## Setup
6+
7+
```bash
8+
npm install
9+
```
10+
11+
## Running
12+
13+
Create a `.env.local` file with your Replane credentials:
14+
15+
```env
16+
# Server-side (not exposed to client)
17+
REPLANE_BASE_URL=https://replane.example.com
18+
REPLANE_SDK_KEY=your-sdk-key
19+
20+
# Client-side (exposed via NEXT_PUBLIC_ prefix)
21+
NEXT_PUBLIC_REPLANE_BASE_URL=https://replane.example.com
22+
NEXT_PUBLIC_REPLANE_SDK_KEY=your-sdk-key
23+
```
24+
25+
Then start the development server:
26+
27+
```bash
28+
npm run dev
29+
```
30+
31+
## What this example demonstrates
32+
33+
- Server-side config fetching with `getReplaneSnapshot()`
34+
- Zero-loading-state hydration with `ReplaneNextProvider`
35+
- `useConfig` hook for reactive client components
36+
- Real-time updates via SSE after hydration
37+
- Type-safe configuration with TypeScript
38+
- Context-based override evaluation
39+
40+
## Architecture
41+
42+
```
43+
┌─────────────────────────────────────────────────────────────┐
44+
│ Server (RSC) │
45+
│ ┌───────────────────────────────────────────────────────┐ │
46+
│ │ getReplaneSnapshot() │ │
47+
│ │ - Fetches configs from Replane API │ │
48+
│ │ - Returns serializable snapshot │ │
49+
│ └───────────────────────────────────────────────────────┘ │
50+
└─────────────────────────────────────────────────────────────┘
51+
52+
▼ snapshot (serialized)
53+
┌─────────────────────────────────────────────────────────────┐
54+
│ Client │
55+
│ ┌───────────────────────────────────────────────────────┐ │
56+
│ │ ReplaneNextProvider │ │
57+
│ │ - Restores client from snapshot (instant) │ │
58+
│ │ - Connects to Replane for real-time updates │ │
59+
│ └───────────────────────────────────────────────────────┘ │
60+
│ │ │
61+
│ ▼ │
62+
│ ┌───────────────────────────────────────────────────────┐ │
63+
│ │ useConfig() / useReplane() │ │
64+
│ │ - Access configs in any client component │ │
65+
│ │ - Auto re-render on config changes │ │
66+
│ └───────────────────────────────────────────────────────┘ │
67+
└─────────────────────────────────────────────────────────────┘
68+
```

examples/next/next-env.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/// <reference types="next" />
2+
/// <reference types="next/image-types/global" />
3+
4+
// NOTE: This file should not be edited
5+
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.

examples/next/next.config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import type { NextConfig } from "next";
2+
3+
const nextConfig: NextConfig = {
4+
/* config options here */
5+
};
6+
7+
export default nextConfig;

examples/next/package.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "replanejs-next-example",
3+
"version": "0.0.0",
4+
"private": true,
5+
"description": "Example Next.js project using @replanejs/next",
6+
"scripts": {
7+
"dev": "next dev",
8+
"build": "next build",
9+
"start": "next start",
10+
"lint": "next lint"
11+
},
12+
"dependencies": {
13+
"@replanejs/next": "^0.1.0",
14+
"@replanejs/react": "^0.1.0",
15+
"@replanejs/sdk": "^0.6.1",
16+
"next": "^15.0.0",
17+
"react": "^18.3.1",
18+
"react-dom": "^18.3.1"
19+
},
20+
"devDependencies": {
21+
"@types/node": "^22.15.17",
22+
"@types/react": "^18.3.12",
23+
"@types/react-dom": "^18.3.1",
24+
"typescript": "^5.8.3"
25+
}
26+
}

examples/next/src/app/globals.css

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
* {
2+
box-sizing: border-box;
3+
margin: 0;
4+
padding: 0;
5+
}
6+
7+
:root {
8+
--primary-color: #3b82f6;
9+
--bg-color: #ffffff;
10+
--text-color: #111827;
11+
--border-color: #e5e7eb;
12+
}
13+
14+
body {
15+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, sans-serif;
16+
line-height: 1.6;
17+
background-color: var(--bg-color);
18+
color: var(--text-color);
19+
min-height: 100vh;
20+
display: flex;
21+
flex-direction: column;
22+
}
23+
24+
main {
25+
flex: 1;
26+
}
27+
28+
.container {
29+
max-width: 900px;
30+
margin: 0 auto;
31+
padding: 2rem;
32+
}
33+
34+
.container h1 {
35+
margin-bottom: 1rem;
36+
}
37+
38+
.container p {
39+
margin-bottom: 1.5rem;
40+
}
41+
42+
code {
43+
background: rgba(0, 0, 0, 0.08);
44+
padding: 0.2em 0.4em;
45+
border-radius: 4px;
46+
font-size: 0.9em;
47+
}
48+
49+
.intro {
50+
background: #f9fafb;
51+
padding: 1.5rem;
52+
border-radius: 8px;
53+
margin-bottom: 2rem;
54+
}
55+
56+
.intro h2 {
57+
margin-bottom: 1rem;
58+
font-size: 1.25rem;
59+
}
60+
61+
.intro ol {
62+
padding-left: 1.5rem;
63+
}
64+
65+
.intro li {
66+
margin-bottom: 0.5rem;
67+
}
68+
69+
/* Header styles */
70+
.header {
71+
padding: 1rem 2rem;
72+
border-bottom: 1px solid var(--border-color);
73+
display: flex;
74+
align-items: center;
75+
justify-content: space-between;
76+
}
77+
78+
.header.new-nav {
79+
border-bottom: 3px solid var(--primary-color);
80+
}
81+
82+
.header h1 {
83+
font-size: 1.25rem;
84+
color: var(--primary-color);
85+
}
86+
87+
.header nav {
88+
display: flex;
89+
gap: 1.5rem;
90+
}
91+
92+
.header nav a {
93+
text-decoration: none;
94+
color: inherit;
95+
opacity: 0.8;
96+
transition: opacity 0.2s;
97+
}
98+
99+
.header nav a:hover {
100+
opacity: 1;
101+
}
102+
103+
/* Footer styles */
104+
.footer {
105+
padding: 1.5rem 2rem;
106+
border-top: 1px solid var(--border-color);
107+
text-align: center;
108+
color: #6b7280;
109+
font-size: 0.875rem;
110+
}
111+
112+
/* Feature showcase */
113+
.feature-showcase {
114+
margin-top: 2rem;
115+
}
116+
117+
.feature-showcase h2 {
118+
margin-bottom: 1rem;
119+
}
120+
121+
.config-grid {
122+
display: grid;
123+
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
124+
gap: 1rem;
125+
margin-bottom: 1.5rem;
126+
}
127+
128+
.config-card {
129+
padding: 1rem;
130+
border: 1px solid var(--border-color);
131+
border-radius: 8px;
132+
}
133+
134+
.config-card h3 {
135+
margin-bottom: 0.5rem;
136+
font-size: 1rem;
137+
color: var(--primary-color);
138+
}
139+
140+
.config-card pre {
141+
background: #f3f4f6;
142+
padding: 0.75rem;
143+
border-radius: 4px;
144+
overflow-x: auto;
145+
font-size: 0.875rem;
146+
}
147+
148+
.hint {
149+
font-size: 0.875rem;
150+
color: #6b7280;
151+
margin-bottom: 1rem;
152+
}
153+
154+
.experimental-banner {
155+
margin-top: 1.5rem;
156+
padding: 1rem;
157+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
158+
color: white;
159+
border-radius: 8px;
160+
}
161+
162+
.experimental-banner h3 {
163+
margin-bottom: 0.25rem;
164+
}

0 commit comments

Comments
 (0)