@variantlab/react-native 0.1.0 → 0.1.1
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.
- package/README.md +166 -14
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,23 +1,175 @@
|
|
|
1
1
|
# @variantlab/react-native
|
|
2
2
|
|
|
3
|
-
React Native and Expo bindings for variantlab.
|
|
3
|
+
> React Native and Expo bindings for variantlab — storage adapters, auto-context, deep links, debug overlay, and QR sharing.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+

|
|
6
|
+

|
|
6
7
|
|
|
7
|
-
##
|
|
8
|
+
## Install
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
```bash
|
|
11
|
+
npm install @variantlab/core@alpha @variantlab/react@alpha @variantlab/react-native@alpha
|
|
12
|
+
```
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
- `react
|
|
14
|
+
**Peer dependencies (required):**
|
|
15
|
+
- `react ^18.2.0 || ^19.0.0`
|
|
16
|
+
- `react-native >=0.74.0`
|
|
13
17
|
|
|
14
|
-
Optional (install what you need)
|
|
18
|
+
**Optional peer dependencies (install what you need):**
|
|
19
|
+
- `@react-native-async-storage/async-storage` — persistent storage
|
|
20
|
+
- `react-native-mmkv` — fast key-value storage
|
|
21
|
+
- `expo-secure-store` — encrypted storage
|
|
22
|
+
- `expo-localization` — locale detection
|
|
23
|
+
- `react-native-safe-area-context` — safe area for debug overlay
|
|
24
|
+
- `react-native-svg` — QR code rendering
|
|
15
25
|
|
|
16
|
-
|
|
17
|
-
- `react-native-mmkv`
|
|
18
|
-
- `expo-secure-store`
|
|
19
|
-
- `expo-localization`
|
|
20
|
-
- `react-native-safe-area-context`
|
|
21
|
-
- `react-native-svg`
|
|
26
|
+
## Quick start
|
|
22
27
|
|
|
23
|
-
|
|
28
|
+
### 1. Create the engine with auto-context
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
import { createEngine } from "@variantlab/core";
|
|
32
|
+
import { getAutoContext, createAsyncStorageAdapter } from "@variantlab/react-native";
|
|
33
|
+
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
34
|
+
import experiments from "./experiments.json";
|
|
35
|
+
|
|
36
|
+
const engine = createEngine(experiments, {
|
|
37
|
+
context: getAutoContext(), // auto-detects platform, screenSize, locale
|
|
38
|
+
storage: createAsyncStorageAdapter(AsyncStorage),
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 2. Wrap your app
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import { VariantLabProvider } from "@variantlab/react-native";
|
|
46
|
+
|
|
47
|
+
export default function App() {
|
|
48
|
+
return (
|
|
49
|
+
<VariantLabProvider engine={engine}>
|
|
50
|
+
<YourApp />
|
|
51
|
+
</VariantLabProvider>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 3. Use hooks (same API as @variantlab/react)
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
import { useVariant, useVariantValue, Variant } from "@variantlab/react-native";
|
|
60
|
+
|
|
61
|
+
function CardLayout() {
|
|
62
|
+
const variant = useVariant("card-layout");
|
|
63
|
+
// ...
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function PriceDisplay() {
|
|
67
|
+
const price = useVariantValue<number>("pricing");
|
|
68
|
+
return <Text>${price}</Text>;
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Auto-context detection
|
|
73
|
+
|
|
74
|
+
`getAutoContext()` automatically detects:
|
|
75
|
+
|
|
76
|
+
| Field | Source |
|
|
77
|
+
|-------|--------|
|
|
78
|
+
| `platform` | `Platform.OS` (`ios`, `android`, `web`) |
|
|
79
|
+
| `screenSize` | `Dimensions.get("window").width` bucketed to `small` / `medium` / `large` |
|
|
80
|
+
| `locale` | `expo-localization` or `NativeModules` |
|
|
81
|
+
| `appVersion` | `expo-constants` or `DeviceInfo` |
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
import { getAutoContext } from "@variantlab/react-native";
|
|
85
|
+
|
|
86
|
+
const context = getAutoContext();
|
|
87
|
+
// { platform: "ios", screenSize: "medium", locale: "en", ... }
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Storage adapters
|
|
91
|
+
|
|
92
|
+
Choose the storage backend that fits your app:
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
import { createAsyncStorageAdapter } from "@variantlab/react-native";
|
|
96
|
+
import { createMMKVStorageAdapter } from "@variantlab/react-native";
|
|
97
|
+
import { createSecureStoreAdapter } from "@variantlab/react-native";
|
|
98
|
+
import { createMemoryStorage } from "@variantlab/react-native";
|
|
99
|
+
|
|
100
|
+
// AsyncStorage — most common, works everywhere
|
|
101
|
+
const storage = createAsyncStorageAdapter(AsyncStorage);
|
|
102
|
+
|
|
103
|
+
// MMKV — faster, synchronous reads
|
|
104
|
+
const storage = createMMKVStorageAdapter(mmkv);
|
|
105
|
+
|
|
106
|
+
// SecureStore — encrypted, for sensitive experiment data
|
|
107
|
+
const storage = createSecureStoreAdapter(SecureStore);
|
|
108
|
+
|
|
109
|
+
// Memory — no persistence, resets on restart (good for tests)
|
|
110
|
+
const storage = createMemoryStorage();
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Debug overlay
|
|
114
|
+
|
|
115
|
+
A built-in bottom-sheet UI for viewing and overriding experiments on device.
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
import { VariantDebugOverlay } from "@variantlab/react-native/debug";
|
|
119
|
+
|
|
120
|
+
export default function App() {
|
|
121
|
+
return (
|
|
122
|
+
<VariantLabProvider engine={engine}>
|
|
123
|
+
<YourApp />
|
|
124
|
+
{__DEV__ && <VariantDebugOverlay />}
|
|
125
|
+
</VariantLabProvider>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The overlay shows:
|
|
131
|
+
- All active experiments and current assignments
|
|
132
|
+
- Tap to override any variant
|
|
133
|
+
- Current targeting context
|
|
134
|
+
- Assignment source (default, hash, override, etc.)
|
|
135
|
+
|
|
136
|
+
## Deep link overrides
|
|
137
|
+
|
|
138
|
+
Allow QA to force variants via deep links:
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
myapp://variantlab?set=hero-layout:split
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
import { registerDeepLinkHandler } from "@variantlab/react-native";
|
|
146
|
+
|
|
147
|
+
// In your app setup
|
|
148
|
+
registerDeepLinkHandler(engine);
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## QR sharing
|
|
152
|
+
|
|
153
|
+
Share experiment state with teammates via QR codes:
|
|
154
|
+
|
|
155
|
+
```tsx
|
|
156
|
+
import { buildQrUrl, parseQrUrl } from "@variantlab/react-native/qr";
|
|
157
|
+
|
|
158
|
+
// Encode current state into a URL
|
|
159
|
+
const url = buildQrUrl(payload);
|
|
160
|
+
|
|
161
|
+
// Parse a scanned QR URL
|
|
162
|
+
const result = parseQrUrl(url);
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## All re-exported hooks and components
|
|
166
|
+
|
|
167
|
+
This package re-exports everything from `@variantlab/react`:
|
|
168
|
+
|
|
169
|
+
**Hooks:** `useVariant`, `useVariantValue`, `useExperiment`, `useSetVariant`, `useVariantLabEngine`, `useRouteExperiments`
|
|
170
|
+
|
|
171
|
+
**Components:** `<Variant>`, `<VariantValue>`, `<VariantErrorBoundary>`, `<VariantLabProvider>`
|
|
172
|
+
|
|
173
|
+
## License
|
|
174
|
+
|
|
175
|
+
[MIT](./LICENSE)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@variantlab/react-native",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "React Native and Expo bindings for variantlab.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -71,8 +71,8 @@
|
|
|
71
71
|
"node": ">=18.17"
|
|
72
72
|
},
|
|
73
73
|
"dependencies": {
|
|
74
|
-
"@variantlab/core": "0.1.
|
|
75
|
-
"@variantlab/react": "0.1.
|
|
74
|
+
"@variantlab/core": "0.1.1",
|
|
75
|
+
"@variantlab/react": "0.1.1"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
78
|
"@react-native-async-storage/async-storage": "*",
|