Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
44 changes: 13 additions & 31 deletions packages/browser-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,13 @@ type Configuration = {
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 Bucket servers. Useful when you're impersonating a user
featureOptions?: {
fallbackFeatures?:
| string[]
| Record<string, { key: string; payload: any } | true>; // Enable these features if unable to contact bucket.co. Can be a list of feature keys or a record with configuration values
timeoutMs?: number; // Timeout for fetching features (default: 5000ms)
staleWhileRevalidate?: boolean; // Revalidate in the background when cached features turn stale to avoid latency in the UI (default: false)
staleTimeMs?: number; // at initialization time features are loaded from the cache unless they have gone stale. Defaults to 0 which means the cache is disabled. Increase in the case of a non-SPA
expireTimeMs?: number; // In case we're unable to fetch features from Bucket, cached/stale features will be used instead until they expire after `expireTimeMs`. Default is 30 days
};
fallbackFeatures?:
| string[]
| Record<string, { key: string; payload: any } | true>; // Enable these features if unable to contact bucket.co. Can be a list of feature keys or a record with configuration values
timeoutMs?: number; // Timeout for fetching features (default: 5000ms)
staleWhileRevalidate?: boolean; // Revalidate in the background when cached features turn stale to avoid latency in the UI (default: false)
staleTimeMs?: number; // at initialization time features are loaded from the cache unless they have gone stale. Defaults to 0 which means the cache is disabled. Increase this in the case of a non-SPA
expireTimeMs?: number; // In case we're unable to fetch features from Bucket, cached/stale features will be used instead until they expire after `expireTimeMs`. Default is 30 days
};
```

Expand Down Expand Up @@ -194,23 +192,6 @@ const override = bucketClient.getFeatureOverride("huddle"); // returns boolean |

Feature overrides are persisted in `localStorage` and will be restored when the page is reloaded.

### Feature Updates

You can listen for feature updates using `onFeaturesUpdated`:

```ts
// Register a callback for feature updates
const unsubscribe = bucketClient.onFeaturesUpdated(() => {
console.log("Features were updated");
});

// Later, stop listening for updates
unsubscribe();
```

> [!NOTE]
> Note that the callback may be called even if features haven't actually changed.

### Remote config

Similar to `isEnabled`, each feature has a `config` property. This configuration is managed from within Bucket.
Expand Down Expand Up @@ -312,11 +293,12 @@ See details in [Feedback HTTP API](https://docs.bucket.co/reference/http-trackin

Event listeners allow for capturing various events occurring in the `BucketClient`. This is useful to build integrations with other system or for various debugging purposes. There are 5 kinds of events:

- FeaturesUpdated
- User
- Company
- Check
- Track
- `configCheck`: Your code used a feature config
- `enabledCheck`: Your code checked whether a specific feature should be enabled
- `featuresUpdated`: Features were updated. Either because they were loaded as part of initialization or because the user/company updated
- `user`: User information updated (similar to the `identify` call used in tracking terminology)
- `company`: Company information updated (sometimes to the `group` call used in tracking terminology)
- `track`: Track event occurred.

Use the `on()` method to add an event listener to respond to certain events. See the API reference for details on each hook.

Expand Down
170 changes: 77 additions & 93 deletions packages/react-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Install via npm:
npm i @bucketco/react-sdk
```

## Setup
## Get started

### 1. Define Features (optional)

Expand Down Expand Up @@ -39,8 +39,7 @@ declare module "@bucketco/react-sdk" {

### 2. Add the `BucketProvider` context provider

Add the `BucketProvider` context provider to your application.
This will initialize the Bucket SDK, fetch features and start listening for automated feedback survey events.
Add the `BucketProvider` context provider to your application:

**Example:**

Expand All @@ -52,107 +51,33 @@ import { BucketProvider } from "@bucketco/react-sdk";
company={{ id: "acme_inc", plan: "pro" }}
user={{ id: "john doe" }}
loadingComponent={<Loading />}
featureOptions={{ fallbackFeatures: ["huddle"] }}
>
{/* children here are shown when loading finishes or immediately if no `loadingComponent` is given */}
</BucketProvider>;
```

- `publishableKey` is used to connect the provider to an _environment_ on Bucket. Find your `publishableKey` under [environment settings](https://app.bucket.co/envs/current/settings/app-environments) in Bucket,
- `company`, `user` and `otherContext` make up the _context_ that is used to determine if a feature is enabled or not. `company` and `user` contexts are automatically transmitted to Bucket servers so the Bucket app can show you which companies have access to which features etc.
> [!Note]
> If you specify `company` and/or `user` they must have at least the `id` property, otherwise they will be ignored in their entirety. You should also supply anything additional you want to be able to evaluate feature targeting against,
- `featureOptions` contains configuration for features:

- `fallbackFeatures`: A list of strings which specify which features to consider enabled if the SDK is unable to fetch features. Can be provided in two formats:

```ts
// Simple array of feature keys
featureOptions={{
fallbackFeatures: ["feature1", "feature2"]
}}

// Or with configuration overrides
featureOptions={{
fallbackFeatures: {
"feature1": true, // just enable the feature
"feature2": { // enable with configuration
key: "variant-a",
payload: {
limit: 100,
mode: "test"
}
}
}
}}
```
### 3. Use `useFeature(<featureKey>)` to get feature status

- `timeoutMs`: Timeout in milliseconds when fetching features from the server,
- `staleWhileRevalidate`: If set to `true`, stale features will be returned while refetching features in the background,
- `expireTimeMs`: If set, features will be cached between page loads for this duration (in milliseconds),
- `staleTimeMs`: Maximum time (in milliseconds) that stale features will be returned if `staleWhileRevalidate` is true and new features cannot be fetched.
Using the `useFeature` hook from your components lets you toggle features on/off and configure features through Remote Config:

Example with all options:
**Example:**

```tsx
<BucketProvider
publishableKey={YOUR_PUBLISHABLE_KEY}
featureOptions={{
// Fallback features if server is unreachable
fallbackFeatures: {
"premium-feature": {
key: "basic",
payload: { maxItems: 10 },
},
},
// Timeout after 5 seconds
timeoutMs: 5000,
// Return stale data while fetching
staleWhileRevalidate: true,
// Cache features for 1 hour
expireTimeMs: 60 * 60 * 1000,
// Allow stale data up to 5 minutes
staleTimeMs: 5 * 60 * 1000,
}}
// ... other props
>
{children}
</BucketProvider>
```

- `loadingComponent` lets you specify an React component to be rendered instead of the children while the Bucket provider is initializing. If you want more control over loading screens, `useFeature()` returns `isLoading` which you can use to customize the loading experience:

```tsx
function LoadingBucket({ children }) {
const { isLoading } = useFeature("myFeature")
if (isLoading) {
return <Spinner />
}
function StartHuddleButton() {
const {
isEnabled, // boolean indicating if the feature is enabled
track, // track usage of the feature
} = useFeature("huddle");

return children
if (!isEnabled) {
return null;
}

//-- Initialize the Bucket provider
<BucketProvider publishableKey={YOUR_PUBLISHABLE_KEY} /*...*/>
<LoadingBucket>
{/* children here are shown when loading finishes */}
</LoadingBucket>
<BucketProvider>
```
return <button onClick={track}>Start huddle!</button>;
}
```

- `enableTracking` (default: `true`): Set to `false` to stop sending tracking events and user/company updates to Bucket. Useful when you're impersonating a user,
- `apiBaseUrl`: Optional base URL for the Bucket API. Use this to override the default API endpoint,
- `appBaseUrl`: Optional base URL for the Bucket application. Use this to override the default app URL,
- `sseBaseUrl`: Optional base URL for Server-Sent Events. Use this to override the default SSE endpoint,
- `debug`: Set to `true` to enable debug logging to the console,
- `toolbar`: Optional configuration for the Bucket toolbar,
- `feedback`: Optional configuration for feedback collection:

```ts
{
enableLiveSatisfaction: boolean; // Enable/disable live satisfaction surveys
}
```
`useFeature` can help you do much more. See a full example for `useFeature` [see below](#usefeature).

## Feature toggles

Expand All @@ -162,7 +87,7 @@ If you supply `user` or `company` objects, they must include at least the `id` p
In addition to the `id`, you must also supply anything additional that you want to be able to evaluate feature targeting rules against.
The additional attributes are supplied using the `otherContext` prop.

Attributes cannot be nested (multiple levels) and must be either strings, integers or booleans.
Attributes cannot be nested (multiple levels) and must be either strings, numbers or booleans.
A number of special attributes exist:

- `name` -- display name for `user`/`company`,
Expand Down Expand Up @@ -212,6 +137,65 @@ configuration in your application.
Note that, similar to `isEnabled`, accessing `config` on the object returned by `useFeature()` automatically
generates a `check` event.

## `<BucketProvider>` component

The `<BucketProvider>` initializes the Bucket SDK, fetches features and starts listening for automated feedback survey events. The component can be configured using a number of props:

- `publishableKey` is used to connect the provider to an _environment_ on Bucket. Find your `publishableKey` under [environment settings](https://app.bucket.co/envs/current/settings/app-environments) in Bucket,
- `company`, `user` and `otherContext` make up the _context_ that is used to determine if a feature is enabled or not. `company` and `user` contexts are automatically transmitted to Bucket servers so the Bucket app can show you which companies have access to which features etc.
> [!Note]
> If you specify `company` and/or `user` they must have at least the `id` property, otherwise they will be ignored in their entirety. You should also supply anything additional you want to be able to evaluate feature targeting against,
- `fallbackFeatures`: A list of strings which specify which features to consider enabled if the SDK is unable to fetch features. Can be provided in two formats:

```ts
// Simple array of feature keys
fallbackFeatures={["feature1", "feature2"]}

// Or with configuration overrides
fallbackFeatures: {
"feature1": true, // just enable the feature
"feature2": { // enable with configuration
key: "variant-a",
payload: {
limit: 100,
mode: "test"
}
}
}
```

- `timeoutMs`: Timeout in milliseconds when fetching features from the server,
- `staleWhileRevalidate`: If set to `true`, stale features will be returned while refetching features in the background,
- `expireTimeMs`: If set, features will be cached between page loads for this duration (in milliseconds),
- `staleTimeMs`: Maximum time (in milliseconds) that stale features will be returned if `staleWhileRevalidate` is true and new features cannot be fetched.
- `loadingComponent` lets you specify an React component to be rendered instead of the children while the Bucket provider is initializing. If you want more control over loading screens, `useFeature()` returns `isLoading` which you can use to customize the loading experience:

```tsx
function LoadingBucket({ children }) {
const { isLoading } = useFeature("myFeature")
if (isLoading) {
return <Spinner />
}

return children
}

//-- Initialize the Bucket provider
<BucketProvider publishableKey={YOUR_PUBLISHABLE_KEY} /*...*/>
<LoadingBucket>
{/* children here are shown when loading finishes */}
</LoadingBucket>
<BucketProvider>
```

- `enableTracking`: Set to `false` to stop sending tracking events and user/company updates to Bucket. Useful when you're impersonating a user (defaults to `true`),
- `apiBaseUrl`: Optional base URL for the Bucket API. Use this to override the default API endpoint,
- `appBaseUrl`: Optional base URL for the Bucket application. Use this to override the default app URL,
- `sseBaseUrl`: Optional base URL for Server-Sent Events. Use this to override the default SSE endpoint,
- `debug`: Set to `true` to enable debug logging to the console,
- `toolbar`: Optional configuration for the Bucket toolbar,
- `feedback`: Optional configuration for feedback collection

## Hooks

### `useFeature()`
Expand Down Expand Up @@ -248,7 +232,7 @@ function StartHuddleButton() {
<button
onClick={(e) =>
requestFeedback({
title: payload?.question ?? "How do you like Huddles?",
title: payload?.question ?? "How do you like the Huddles feature?",
position: {
type: "POPOVER",
anchor: e.currentTarget as HTMLElement,
Expand Down
3 changes: 0 additions & 3 deletions packages/react-sdk/dev/plain/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,6 @@ export function App() {
return (
<BucketProvider
publishableKey={publishableKey}
feedback={{
enableLiveSatisfaction: true,
}}
company={initialCompany}
user={initialUser}
otherContext={initialOtherContext}
Expand Down