Skip to content

Commit ca99a76

Browse files
committed
adding react sdk
1 parent c67f7f9 commit ca99a76

File tree

4 files changed

+153
-0
lines changed

4 files changed

+153
-0
lines changed

packages/sdks/react/index.tsx

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/**
2+
* OpenPanel React Provider
3+
*
4+
* A React context provider for OpenPanel analytics that provides a clean,
5+
* idiomatic way to use OpenPanel in React/Next.js applications.
6+
*
7+
* This is temporary until OpenPanel publishes an official React/Next.js package.
8+
*
9+
* @see https://openpanel.dev
10+
*/
11+
12+
'use client';
13+
14+
import type { OpenPanelOptions } from '@openpanel/sdk';
15+
import { OpenPanel as OpenPanelBase } from '@openpanel/sdk';
16+
import { type ReactNode, createContext, useContext, useRef } from 'react';
17+
18+
export * from '@openpanel/sdk';
19+
20+
/**
21+
* React-specific OpenPanel client
22+
*/
23+
export class OpenPanel extends OpenPanelBase {
24+
constructor(options: OpenPanelOptions) {
25+
super({
26+
...options,
27+
sdk: 'react',
28+
sdkVersion: '19.0.0',
29+
});
30+
}
31+
}
32+
33+
/**
34+
* Configuration options for the OpenPanel provider
35+
*/
36+
interface OpenPanelProviderProps extends Omit<OpenPanelOptions, 'clientId'> {
37+
/** React children to render */
38+
children: ReactNode;
39+
/** OpenPanel client ID. Falls back to environment variables if not provided */
40+
clientId?: string;
41+
}
42+
43+
const OpenPanelContext = createContext<OpenPanel | null>(null);
44+
45+
/**
46+
* Hook to access the OpenPanel client instance
47+
*
48+
* @throws {Error} If used outside of OpenPanelProvider
49+
* @returns OpenPanel client instance or null if not initialized
50+
*
51+
* @example
52+
* ```tsx
53+
* const openpanel = useOpenPanel();
54+
* openpanel?.track('button_clicked', { label: 'Subscribe' });
55+
* ```
56+
*/
57+
export function useOpenPanel() {
58+
const context = useContext(OpenPanelContext);
59+
if (context === undefined) {
60+
throw new Error('useOpenPanel must be used within OpenPanelProvider');
61+
}
62+
return context;
63+
}
64+
65+
/**
66+
* Provider component that initializes and provides OpenPanel to child components
67+
*
68+
* Automatically reads from environment variables:
69+
* - NEXT_PUBLIC_OPENPANEL_CLIENT_ID
70+
* - OPENPANEL_CLIENT_ID
71+
*
72+
* @param children - React children to render
73+
* @param clientId - OpenPanel client ID. Falls back to environment variables if not provided
74+
* @param options - Additional OpenPanel configuration options
75+
*
76+
* @example
77+
* ```tsx
78+
* <OpenPanelProvider clientId="your-client-id">
79+
* <App />
80+
* </OpenPanelProvider>
81+
* ```
82+
*/
83+
export function OpenPanelProvider({
84+
children,
85+
clientId,
86+
...options
87+
}: OpenPanelProviderProps) {
88+
const openpanelRef = useRef<OpenPanel | null>(null);
89+
90+
if (!openpanelRef.current && typeof window !== 'undefined') {
91+
const id =
92+
clientId ||
93+
process.env.NEXT_PUBLIC_OPENPANEL_CLIENT_ID ||
94+
process.env.OPENPANEL_CLIENT_ID ||
95+
'';
96+
97+
if (id) {
98+
openpanelRef.current = new OpenPanel({
99+
clientId: id,
100+
...options,
101+
});
102+
}
103+
}
104+
105+
return (
106+
<OpenPanelContext.Provider value={openpanelRef.current}>
107+
{children}
108+
</OpenPanelContext.Provider>
109+
);
110+
}

packages/sdks/react/package.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "@openpanel/react",
3+
"version": "1.0.1-local",
4+
"module": "index.ts",
5+
"scripts": {
6+
"build": "rm -rf dist && tsup",
7+
"typecheck": "tsc --noEmit"
8+
},
9+
"dependencies": {
10+
"@openpanel/sdk": "workspace:1.0.0-local"
11+
},
12+
"peerDependencies": {
13+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
14+
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
15+
},
16+
"devDependencies": {
17+
"@openpanel/tsconfig": "workspace:*",
18+
"@types/node": "catalog:",
19+
"@types/react": "catalog:",
20+
"tsup": "^7.2.0",
21+
"typescript": "catalog:"
22+
}
23+
}

packages/sdks/react/tsconfig.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": "@openpanel/tsconfig/base.json",
3+
"compilerOptions": {
4+
"incremental": false,
5+
"outDir": "dist"
6+
},
7+
"exclude": ["dist"]
8+
}

packages/sdks/react/tsup.config.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { defineConfig } from 'tsup';
2+
3+
export default defineConfig({
4+
format: ['cjs', 'esm'],
5+
entry: ['index.tsx'],
6+
external: ['react', 'react-dom'],
7+
dts: true,
8+
splitting: false,
9+
sourcemap: false,
10+
clean: true,
11+
minify: true,
12+
});

0 commit comments

Comments
 (0)