Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
1e5a65a
feat: add ability to bootstrap flags on server and populate clients
Swiftwork Sep 12, 2025
8d7f44c
docs: add readme for new getFlagsForBootstrap method
Swiftwork Sep 15, 2025
d9b307f
docs: add code comments to react sdk
Swiftwork Sep 15, 2025
462e952
Merge branch 'main' into feat/bootstrapped-flags
Swiftwork Sep 15, 2025
090d631
fix: export types
Swiftwork Sep 15, 2025
2d5954d
refactor: use bootstrapped flags to determine if bootstrap is needed
Swiftwork Sep 15, 2025
03c92ab
fix: separate bootstrap type from regular
Swiftwork Sep 16, 2025
ab86bd3
test: fix
Swiftwork Sep 16, 2025
64db7eb
docs: browser SDK
Swiftwork Sep 16, 2025
e06b1b9
fix: type and setContext
Swiftwork Sep 16, 2025
1ca0e7f
feat: update ReflagProviders and add documentation
Swiftwork Sep 16, 2025
9fd6c00
docs: move bootstrapFlags to below remoteConfig
Swiftwork Sep 16, 2025
bb6e060
fix: update canonical-json to match vue library
Swiftwork Sep 16, 2025
8293c42
fix: build
Swiftwork Sep 16, 2025
e7f8499
test: fix
Swiftwork Sep 16, 2025
c452029
refactor: cleaned up types
Swiftwork Sep 16, 2025
0749050
fix: remove type keys
Swiftwork Sep 16, 2025
b6c69dd
docs: standardize docs
Swiftwork Sep 16, 2025
d70ca0d
docs: key benefits added
Swiftwork Sep 16, 2025
83625d8
feat: added useIsLoading hook similar to vue
Swiftwork Sep 16, 2025
3e11987
docs: fix order
Swiftwork Sep 16, 2025
29e774e
feat: update vue to new pattern
Swiftwork Sep 16, 2025
d0fb7da
docs: add app router section
Swiftwork Sep 16, 2025
e283941
feat: nextjs-boostrap-demo
Swiftwork Sep 16, 2025
c66d271
Merge branch 'main' into feat/bootstrapped-flags
Swiftwork Sep 16, 2025
f5ac61a
fix: offline mode when run in CI
Swiftwork Sep 16, 2025
3380191
Merge branch 'feat/bootstrapped-flags' of https://github.com/bucketco…
Swiftwork Sep 16, 2025
71aa439
fix: bug, warning, versions, logs
Swiftwork Sep 17, 2025
38d5a4d
fix: update docs and examples
Swiftwork Sep 17, 2025
4640f2f
chore: cleanup
Swiftwork Sep 17, 2025
c097336
feat: rewrite to new provider pattern and cookie overrides
Swiftwork Sep 19, 2025
80c7220
feat: cleanup context types
Swiftwork Sep 22, 2025
a8993b0
feat: graceful upgrade to server side overrides
Swiftwork Sep 23, 2025
f5d5727
test: fix browser tests
Swiftwork Sep 23, 2025
f96796a
test: fix configs
Swiftwork Sep 23, 2025
fd3fdda
test: fix browser and client tests
Swiftwork Sep 23, 2025
35dc2fc
test: add test for cookie parsing
Swiftwork Sep 23, 2025
cb752c9
refactor: using overrides provider
Swiftwork Sep 24, 2025
95f725d
Merge branch 'main' into feat/bootstrapped-flags
Swiftwork Sep 24, 2025
5779777
fix: types and warnings
Swiftwork Sep 24, 2025
fb8fb9e
Merge branch 'feat/bootstrapped-flags' of https://github.com/bucketco…
Swiftwork Sep 24, 2025
2b8e781
fix: pr comments
Swiftwork Sep 24, 2025
862e076
refactor: remove overrides provider and overrides passed to server
Swiftwork Sep 24, 2025
2c0d54f
fix: scripts
Swiftwork Sep 24, 2025
7232a89
feat: refactored vue sdk into same format as react
Swiftwork Sep 25, 2025
59ee8dc
test: remove obsolete test
Swiftwork Sep 25, 2025
630f848
docs: update readmes
Swiftwork Sep 25, 2025
ee1935d
revert: move prop types back into shared file
Swiftwork Sep 25, 2025
5cb8c91
chore: remove obsolete package
Swiftwork Sep 25, 2025
2c12569
fix: type, exports, examples, and docs
Swiftwork Sep 25, 2025
efaa2bb
style: fix lint errors
Swiftwork Sep 25, 2025
fc0aa04
fix: PR comments
Swiftwork Sep 26, 2025
db68687
fix: docs and issues
Swiftwork Sep 26, 2025
0eee286
chore: remove obsolete console log
Swiftwork Sep 26, 2025
bafcc82
style: slight watch cleanup
Swiftwork Sep 26, 2025
b1841a4
test: fix node tests
Swiftwork Sep 26, 2025
a95a399
fix: nextjs no-store build error
Swiftwork Sep 29, 2025
6b9a329
feat: update yarn version
Swiftwork Sep 29, 2025
b8b68fc
fix: types
Swiftwork Sep 29, 2025
ec69d44
chore: remove yarn path
Swiftwork Sep 29, 2025
c064d5c
ci: add corepack to ci
Swiftwork Sep 29, 2025
2d1f842
ci: change order of job steps
Swiftwork Sep 29, 2025
272c8aa
fix: initial loading condition
Swiftwork Oct 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/package-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout source code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Enable corepack
run: corepack enable
- name: Use Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
cache: "yarn"
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
# Setup .npmrc file to publish to npm
- uses: actions/setup-node@v3
- name: Enable corepack
run: corepack enable
- uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
cache: "yarn"
Expand Down
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[yaml]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
Expand All @@ -32,6 +35,7 @@
"**/node_modules": true
},
"search.exclude": {
"**/.next": true,
"**/build": true,
"**/dist": true,
"**/coverage": true,
Expand Down
893 changes: 0 additions & 893 deletions .yarn/releases/yarn-4.1.1.cjs

This file was deleted.

2 changes: 0 additions & 2 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
nodeLinker: node-modules

yarnPath: .yarn/releases/yarn-4.1.1.cjs
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"version": "lerna version --exact --no-push",
"docs": "./docs.sh"
},
"packageManager": "yarn@4.1.1",
"packageManager": "yarn@4.10.3",
"devDependencies": {
"lerna": "^8.1.3",
"prettier": "^3.5.2",
Expand Down
117 changes: 116 additions & 1 deletion packages/browser-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,71 @@ const flags = reflagClient.getFlags();
Just as `isEnabled`, accessing `config` on the object returned by `getFlags` does not automatically
generate a `check` event, contrary to the `config` property on the object returned by `getFlag`.

## Updating user/company/other context
## Server-side rendering and bootstrapping

For server-side rendered applications, you can eliminate the initial network request by bootstrapping the client with pre-fetched flag data.

### Init options bootstrapped

```typescript
type Configuration = {
logger: console; // by default only logs warn/error, by passing `console` you'll log everything
apiBaseUrl?: "https://front.reflag.com";
sseBaseUrl?: "https://livemessaging.bucket.co";
feedback?: undefined; // See FEEDBACK.md
enableTracking?: true; // set to `false` to stop sending track events and user/company updates to Reflag servers. Useful when you're impersonating a user
offline?: boolean; // Use the SDK in offline mode. Offline mode is useful during testing and local development
bootstrappedFlags?: FetchedFlags; // Pre-fetched flags from server-side (see Server-side rendering section)
};
```

### Using bootstrappedFlags

Use the Node SDK's `getFlagsForBootstrap()` method to pre-fetch flags server-side, then pass them to the browser client:

```typescript
// Server-side: Get flags using Node SDK
import { ReflagClient as ReflagNodeClient } from "@reflag/node-sdk";

const serverClient = new ReflagNodeClient({ secretKey: "your-secret-key" });
await serverClient.initialize();

const { flags } = serverClient.getFlagsForBootstrap({
user: { id: "user123", name: "John Doe", email: "john@acme.com" },
company: { id: "company456", name: "Acme Inc", plan: "enterprise" },
});

// Pass flags data to client using your framework's preferred method
// or for example in a script tag
app.get("/", (req, res) => {
res.set("Content-Type", "text/html");
res.send(
Buffer.from(
`<script>var flags = ${JSON.stringify(flags)};</script>
<main id="app"></main>`,
),
);
});

// Client-side: Initialize with pre-fetched flags
import { ReflagClient } from "@reflag/browser-sdk";

const reflagClient = new ReflagClient({
publishableKey: "your-publishable-key",
user: { id: "user123", name: "John Doe", email: "john@acme.com" },
company: { id: "company456", name: "Acme Inc", plan: "enterprise" },
bootstrappedFlags: flags, // No network request needed
});

await reflagClient.initialize(); // Initializes all but flags
const { isEnabled } = reflagClient.getFlag("huddle");
```

This eliminates loading states and improves performance by avoiding the initial flags API call.

## Context management

### Updating user/company/other context

Attributes given for the user/company/other context in the ReflagClient constructor can be updated for use in flag targeting evaluation with the `updateUser()`, `updateCompany()` and `updateOtherContext()` methods.
They return a promise which resolves once the flags have been re-evaluated follow the update of the attributes.
Expand All @@ -244,6 +308,57 @@ await reflagClient.updateUser({ voiceHuddleOptIn: (!isEnabled).toString() });

> [!NOTE] > `user`/`company` attributes are also stored remotely on the Reflag servers and will automatically be used to evaluate flag targeting if the page is refreshed.

### setContext()

The `setContext()` method allows you to replace the entire context (user, company, and other attributes) at once. This method is useful when you need to completely change the context, such as when a user logs in or switches between different accounts.

```ts
await reflagClient.setContext({
user: {
id: "new-user-123",
name: "Jane Doe",
email: "jane@example.com",
role: "admin",
},
company: {
id: "company-456",
name: "New Company Inc",
plan: "enterprise",
},
other: {
feature: "beta",
locale: "en-US",
},
});
```

The method will:

- Replace the entire context with the new values
- Re-evaluate all flags based on the new context
- Update the user and company information on Reflag servers
- Return a promise that resolves once the flags have been re-evaluated

### getContext()

The `getContext()` method returns the current context being used for flag evaluation. This is useful for debugging or when you need to inspect the current user, company, and other attributes.

```ts
const currentContext = reflagClient.getContext();
console.log(currentContext);
// {
// user: { id: "user-123", name: "John Doe", email: "john@example.com" },
// company: { id: "company-456", name: "Acme Inc", plan: "enterprise" },
// other: { locale: "en-US", feature: "beta" }
// }
```

The returned context object contains:

- `user`: Current user attributes (if any)
- `company`: Current company attributes (if any)
- `other`: Additional context attributes not related to user or company

## Toolbar

The Reflag Toolbar is great for toggling flags on/off for yourself to ensure that everything works both when a flag is on and when it's off.
Expand Down
2 changes: 1 addition & 1 deletion packages/browser-sdk/example/typescript/app.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ReflagClient, CheckEvent, RawFlags } from "../../src";
import { ReflagClient, RawFlags } from "../../src";

const urlParams = new URLSearchParams(window?.location?.search);
const publishableKey = urlParams.get("publishableKey");
Expand Down
29 changes: 20 additions & 9 deletions packages/browser-sdk/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
const urlParams = new URLSearchParams(window.location.search);
const publishableKey = urlParams.get("publishableKey");
const flagKey = urlParams.get("flagKey") ?? "huddles";
const isBootstrapped = urlParams.get("bootstrapped") === "true";
</script>
<style>
body {
Expand Down Expand Up @@ -47,11 +48,29 @@
placement: "bottom-right",
},
},
bootstrappedFlags: isBootstrapped
? {
[flagKey]: {
key: flagKey,
isEnabled: true,
},
}
: undefined,
});

function setVisibility(isVisible) {
const startHuddleElem = document.getElementById("start-huddle");
if (startHuddleElem)
startHuddleElem.style.display = isVisible ? "block" : "none";
}

reflag.initialize().then(() => {
console.log("Reflag initialized");
document.getElementById("loading").style.display = "none";
if (isBootstrapped) {
const flag = reflag.getFlag(flagKey);
setVisibility(flag.isEnabled);
}
});

reflag.on("check", (check) =>
Expand All @@ -61,15 +80,7 @@
reflag.on("flagsUpdated", (flags) => {
console.log("Flags updated");
const flag = reflag.getFlag(flagKey);

const startHuddleElem = document.getElementById("start-huddle");
if (flag.isEnabled) {
// show the start-huddle button
if (startHuddleElem) startHuddleElem.style.display = "block";
} else {
// hide the start-huddle button
if (startHuddleElem) startHuddleElem.style.display = "none";
}
setVisibility(flag.isEnabled);
});
</script>
</body>
Expand Down
13 changes: 7 additions & 6 deletions packages/browser-sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@reflag/browser-sdk",
"version": "1.1.0",
"version": "1.2.0",
"packageManager": "yarn@4.1.1",
"license": "MIT",
"repository": {
Expand All @@ -13,15 +13,16 @@
"scripts": {
"dev": "vite",
"build": "tsc --project tsconfig.build.json && vite build",
"test": "tsc --project tsconfig.json && vitest -c vitest.config.ts",
"test": "vitest run",
"test:watch": "vitest",
"test:e2e": "yarn build && playwright test",
"test:ci": "tsc --project tsconfig.json && vitest run -c vitest.config.ts --reporter=default --reporter=junit --outputFile=junit.xml && yarn test:e2e",
"coverage": "vitest run --coverage",
"test:ci": "yarn test --reporter=default --reporter=junit --outputFile=junit.xml && yarn test:e2e",
"coverage": "yarn test --coverage",
"lint": "eslint .",
"lint:ci": "eslint --output-file eslint-report.json --format json .",
"prettier": "prettier --check .",
"format": "yarn lint --fix && yarn prettier --write",
"preversion": "yarn lint && yarn prettier && yarn vitest run -c vitest.config.ts && yarn build"
"preversion": "yarn lint && yarn prettier && yarn test && yarn build"
},
"files": [
"dist"
Expand All @@ -37,7 +38,7 @@
},
"dependencies": {
"@floating-ui/dom": "^1.6.8",
"canonical-json": "^0.0.4",
"fast-equals": "^5.2.2",
"js-cookie": "^3.0.5",
"preact": "^10.22.1"
},
Expand Down
Loading