@gadgetinc/react-chatgpt-apps
v0.3.1
Published
<div align="center"> <p> <img alt="Gadget logo" src="https://raw.githubusercontent.com/gadget-inc/js-clients/main/docs/assets/gadget-logo.png" /> </p> <p> <a href=""> <img alt="GitHub CI status" src="https://badgen.net/github/checks/ga
Keywords
Readme
@gadgetinc/react-chatgpt-apps is a React library for connecting ChatGPT Apps to Gadget backend applications. It provides:
- A
<Provider>component that automatically authenticates your ChatGPT App with your Gadget backend - Easy integration with
@gadgetinc/reacthooks for reading and writing data - Automatic token management using OpenAI's and Gadget's authentication system
When building a ChatGPT App that needs to interact with a Gadget backend, this library handles all the authentication complexity for you, allowing your React components to focus on building great user experiences.
Installation
@gadgetinc/react-chatgpt-apps is a companion package to your Gadget app's JavaScript client and @gadgetinc/react, so you need to install all three packages.
First, set up the Gadget NPM registry and install your app's client:
npm config set @gadget-client:registry https://registry.gadget.dev/npm
# then install your app's client
npm install @gadget-client/your-chatgpt-app-slugFull installation instructions for your app's client can be found in the Gadget docs at https://docs.gadget.dev/api/<your-app-slug>/external-api-calls/installing.
Then, install the React libraries:
npm install @gadgetinc/react @gadgetinc/react-chatgpt-apps react
# or
yarn add @gadgetinc/react @gadgetinc/react-chatgpt-apps reactSetup
To use this library, wrap your ChatGPT App's React components in the Provider component from this package. The Provider automatically handles authentication with your Gadget backend using OpenAI's authentication system.
// in web/chatgpt-widgets/root.tsx
import { Provider } from "@gadgetinc/react-chatgpt-apps";
import { api } from "../api";
function App() {
return (
<Provider api={api}>
<YourChatGPTAppComponents />
</Provider>
);
}
export default App;That's it! The Provider component will:
- Automatically fetch an authentication token from OpenAI when your app loads
- Configure your Gadget API client to use this token for all requests
- Ensure all API calls wait for authentication to be ready before proceeding
Example usage
Once you've wrapped your app in the Provider, you can use all the hooks from @gadgetinc/react to interact with your Gadget backend:
import { useAction, useFindMany } from "@gadgetinc/react";
import { api } from "../../api";
function TaskList() {
// Fetch tasks from your Gadget backend - authentication is handled automatically
const [{ data: tasks, fetching, error }] = useFindMany(api.task, {
select: {
id: true,
title: true,
completed: true,
},
});
// Set up an action to mark tasks as complete
const [_, completeTask] = useAction(api.task.complete);
if (fetching) return <div>Loading tasks...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{tasks.map((task) => (
<li key={task.id}>
<input type="checkbox" checked={task.completed} onChange={() => completeTask({ id: task.id })} />
{task.title}
</li>
))}
</ul>
);
}Authentication
When a user installs your ChatGPT App, they will authenticate themselves with your Gadget backend, and establish some kind of user identity in your session model, user model, or similar. When you use this provider, API calls made from your Widgets will act with that same identity by re-using the same JWT token that your Gadget backend provisioned at install time.
Using without authentication
If you don't need authentication for your ChatGPT widgets such that they are safe to be world readable, you can disable authentication in the provider by setting the authenticate prop to false:
// in web/chatgpt-widgets/root.tsx
import { Provider } from "@gadgetinc/react-chatgpt-apps";
import { api } from "../api";
function App() {
return (
<Provider api={api} authenticate={false}>
<YourChatGPTAppComponents />
</Provider>
);
}
export default App;How it works
ChatGPT Apps use a special authentication mechanism provided by OpenAI. When your app loads in ChatGPT, it can request an authentication token from OpenAI that identifies the current user and conversation. This library:
- Calls OpenAI's
callToolfunction with the special__getGadgetAuthTokenV1tool to retrieve an authentication token - Configures your Gadget API client to include this token in all HTTP requests as a
Bearertoken - Ensures that any API calls made before the token is fetched will wait for authentication to be ready
This all happens automatically when you wrap your app in the Provider component. Your Gadget backend will receive the authenticated requests and can use the token to identify the user and enforce permissions.
API Documentation
<Provider api={api}>
The Provider component must wrap your ChatGPT App to enable authenticated communication with your Gadget backend.
Props:
api(required): An instance of your Gadget application's API client. Example:new Client()whereClientis imported from@gadget-client/your-app-slug.children(required): Your React components that will use the Gadget API.
Example:
import { Client } from "@gadget-client/my-chatgpt-app";
import { Provider } from "@gadgetinc/react-chatgpt-apps";
const api = new Client();
export function App() {
return (
<Provider api={api}>
<YourComponents />
</Provider>
);
}The Provider component:
- Automatically fetches an authentication token from OpenAI when mounted
- Configures your API client to use this token for all requests
- Handles token management transparently - you don't need to manually pass tokens around
- Ensures all API calls wait for authentication to be ready
Using with @gadgetinc/react hooks
Once your app is wrapped in the Provider, you can use all the hooks from @gadgetinc/react to interact with your Gadget backend. All requests will automatically include the authentication token.
See the @gadgetinc/react documentation for the full list of available hooks including:
useFindOne- Fetch a single record by IDuseFindMany- Fetch a list of records with filtering, sorting, and paginationuseAction- Run actions on your Gadget modelsuseGlobalAction- Run global actionsuseFetch- Make custom HTTP requests to your Gadget backend
All of these hooks will work seamlessly with the ChatGPT Apps authentication provided by this package.
ChatGPT-Specific Hooks
This package also provides hooks for interacting with the ChatGPT environment and OpenAI APIs. These hooks enable your app to respond to the ChatGPT context and provide a native experience.
useDisplayMode()
Access the current display mode of your ChatGPT app.
import { useDisplayMode } from "@gadgetinc/react-chatgpt-apps";
function MyWidget() {
const displayMode = useDisplayMode();
return <div>{displayMode === "fullscreen" ? <FullScreenView /> : <CompactView />}</div>;
}Returns one of:
"pip"- Picture-in-picture mode (small floating window)"inline"- Inline mode (embedded in the chat)"fullscreen"- Fullscreen mode (takes up the entire screen)null- If not available
useMaxHeight()
Get the maximum height available for your app in pixels.
import { useMaxHeight } from "@gadgetinc/react-chatgpt-apps";
function MyWidget() {
const maxHeight = useMaxHeight();
return <div style={{ maxHeight }}>Content that respects viewport limits</div>;
}This value updates dynamically as the display mode or window size changes.
useRequestDisplayMode()
Request a change to the app's display mode.
import { useRequestDisplayMode } from "@gadgetinc/react-chatgpt-apps";
function MyWidget() {
const requestDisplayMode = useRequestDisplayMode();
const goFullscreen = async () => {
const result = await requestDisplayMode("fullscreen");
console.log("Granted mode:", result.mode);
};
return <button onClick={goFullscreen}>Go Fullscreen</button>;
}Note: The host may reject or modify the request (e.g., on mobile, PiP is always coerced to fullscreen).
useWidgetState(defaultState)
Manage persistent widget state that syncs with ChatGPT's storage system.
import { useWidgetState } from "@gadgetinc/react-chatgpt-apps";
function Counter() {
const [state, setState] = useWidgetState({ count: 0 });
return <button onClick={() => setState({ count: (state?.count ?? 0) + 1 })}>Count: {state?.count ?? 0}</button>;
}Similar to React's useState, but state persists across re-renders and display mode changes.
useWidgetProps(defaultProps)
Access the tool output props passed to your ChatGPT app.
import { useWidgetProps } from "@gadgetinc/react-chatgpt-apps";
function MyWidget() {
const props = useWidgetProps({ userId: "unknown", theme: "light" });
return <div>User ID: {props.userId}</div>;
}When ChatGPT invokes your app, it may provide initial data through the toolOutput property. This hook retrieves that data with optional defaults.
useSendMessage()
Send follow-up messages to the ChatGPT conversation programmatically.
import { useSendMessage } from "@gadgetinc/react-chatgpt-apps";
function QuickActions() {
const sendMessage = useSendMessage();
return <button onClick={() => sendMessage("Tell me more about this")}>Ask for more details</button>;
}Useful for creating interactive experiences that guide the conversation.
useOpenExternal()
Open external URLs in a way that respects the ChatGPT environment.
import { useOpenExternal } from "@gadgetinc/react-chatgpt-apps";
function LinkButton() {
const openExternal = useOpenExternal();
return <button onClick={() => openExternal("https://example.com")}>Visit Example</button>;
}Attempts to use ChatGPT's native link handler, falling back to window.open if unavailable.
useCallTool()
Call external tools/APIs that have been configured in your ChatGPT app.
import { useCallTool } from "@gadgetinc/react-chatgpt-apps";
function WeatherWidget() {
const callTool = useCallTool();
const fetchWeather = async () => {
const response = await callTool("weatherApi", { city: "San Francisco" });
console.log(response?.result);
};
return <button onClick={fetchWeather}>Get Weather</button>;
}useOpenAiGlobal(key)
Low-level hook for accessing OpenAI global values by key.
import { useOpenAiGlobal } from "@gadgetinc/react-chatgpt-apps";
function ThemeAwareComponent() {
const theme = useOpenAiGlobal("theme");
const locale = useOpenAiGlobal("locale");
return <div className={theme}>Content in {locale}</div>;
}Most apps should use the more specific hooks like useDisplayMode, useMaxHeight, etc. instead of using this hook directly.
Authentication Flow
When your ChatGPT App loads, the following happens automatically:
- The
Providercomponent calls OpenAI'scallToolfunction with the__getGadgetAuthTokenV1tool name - OpenAI returns an authentication token specific to the current user and conversation
- The
Providerconfigures your Gadget API client to include this token as aBearertoken in theAuthorizationheader of all HTTP requests - Your Gadget backend receives the token and can use it to identify the user and enforce permissions
If the token fetch fails (for example, if the app is not running in a ChatGPT environment), an error will be thrown. This ensures your app doesn't make unauthenticated requests by mistake.
