@fluid-app/portal-sdk
v0.1.462
Published
SDK for building custom Fluid portals
Keywords
Readme
@fluid-app/portal-sdk
SDK for building custom Fluid portals. Provides React hooks, providers, and an API client for integrating with the Fluid Commerce platform.
Installation
npm install @fluid-app/portal-sdk
# or
pnpm add @fluid-app/portal-sdk
# or
bun add @fluid-app/portal-sdkPeer Dependencies
This package requires the following peer dependencies:
{
"react": ">=18.0.0",
"@tanstack/react-query": ">=5.0.0"
}Quick Start
Wrap your application with FluidProvider:
import { FluidProvider } from "@fluid-app/portal-sdk";
function App() {
return (
<FluidProvider
config={{
baseUrl: "https://api.fluid.app/api",
getAuthToken: () => localStorage.getItem("fluid_token"),
onAuthError: () => {
// Handle 401 errors (e.g., redirect to login)
window.location.href = "/login";
},
}}
>
<YourApp />
</FluidProvider>
);
}Remote widget architecture
Remote widgets cross a runtime boundary: widget code runs in a worker while the portal SDK runs in the browser host. The implementation is split by ownership:
src/widgets/remote/contract/owns the shared, JSON-safe protocol: capability declarations, portal-function metadata, serializable types, and custom-element event contracts.src/widgets/remote/worker/owns worker authoring and execution:defineWidget,defineWidgetPackage,startWidgetPackage, and worker-side element wrappers.src/widgets/remote/host/owns portal integration: loading remote packages, capability handlers, registry providers, host element registration, and fullscreen coordination.
The dependency direction is worker → contract ← host. Worker code must never
import host code or host-only dependencies such as portal providers, DOM
renderers, or host capability adapters.
Public facades
@fluid-app/portal-sdk/widgets/workeris the stable worker-safe authoring facade. Widget packages should import worker APIs only from this subpath.@fluid-app/portal-sdkis the host application facade. It exposes portal setup and the shared contract types needed to configure the host.- Existing exported host utilities, such as
@fluid-app/portal-sdk/utils/build-widget-registry, remain compatibility facades over the host implementation. Paths belowsrc/widgets/remote/are internal and are not package entry points.
Glossary
- Contract — Shared serializable vocabulary understood by both runtimes; it contains no host implementation.
- Worker — The isolated runtime that renders a remote widget and calls declared portal functions.
- Host — The portal browser runtime that loads workers and implements their declared capabilities.
- Capability — A versioned group of portal functions a widget declares and the host may implement.
- Facade — A stable package entry that re-exports an internal implementation without exposing its directory layout.
Hooks
useFluidProfile
Fetch the portal profile (themes, navigation, screens):
import { useFluidProfile } from "@fluid-app/portal-sdk";
function Navigation() {
const { data: profile, isLoading } = useFluidProfile();
if (isLoading) return <Spinner />;
return (
<nav>
{profile?.navigation.navigation_items.map((item) => (
<NavItem key={item.id} item={item} />
))}
</nav>
);
}useFluidTheme
Control theme settings:
import { useFluidTheme } from "@fluid-app/portal-sdk";
import type { Theme } from "@fluid-app/portal-sdk";
function ThemeSwitcher({ themes }: { themes: Theme[] }) {
const { currentTheme, setTheme, setThemeMode, mode } = useFluidTheme();
return (
<div>
<select
value={currentTheme?.name}
onChange={(e) => {
const theme = themes.find((t) => t.name === e.target.value);
if (theme) setTheme(theme);
}}
>
{themes.map((theme) => (
<option key={theme.name} value={theme.name}>
{theme.name}
</option>
))}
</select>
<button onClick={() => setThemeMode(mode === "dark" ? "light" : "dark")}>
Toggle {mode === "dark" ? "Light" : "Dark"} Mode
</button>
</div>
);
}Providers
FluidProvider
The main provider that sets up the SDK. It wraps:
QueryClientProvider(TanStack Query)FluidThemeProvider(theme management)
import { FluidProvider } from "@fluid-app/portal-sdk";
import { QueryClient } from "@tanstack/react-query";
// Optional: provide your own QueryClient
const queryClient = new QueryClient({
defaultOptions: {
queries: { staleTime: 5 * 60 * 1000 },
},
});
function App() {
return (
<FluidProvider
config={{
baseUrl: "https://api.fluid.app/api",
getAuthToken: () => getToken(),
}}
queryClient={queryClient}
initialTheme={defaultTheme}
>
<YourApp />
</FluidProvider>
);
}FluidThemeProvider
Can be used standalone if you need theme management without the full SDK:
import { FluidThemeProvider, useFluidTheme } from "@fluid-app/portal-sdk";
function App() {
return (
<FluidThemeProvider initialTheme={myTheme}>
<ThemedContent />
</FluidThemeProvider>
);
}Theme CSS variables are applied to the document root (or a custom container) with the --fluid- prefix.
Types
All types are exported for use in your application:
import type {
// Core types
Profile,
Navigation,
NavigationItem,
ScreenDefinition,
// Client types
FluidSDKConfig,
RequestOptions,
} from "@fluid-app/portal-sdk";Query Keys
Query keys are exported for cache invalidation and prefetching:
import { PROFILE_QUERY_KEY } from "@fluid-app/portal-sdk";
import { useQueryClient } from "@tanstack/react-query";
function RefreshButton() {
const queryClient = useQueryClient();
const handleRefresh = () => {
queryClient.invalidateQueries({ queryKey: PROFILE_QUERY_KEY });
};
return <button onClick={handleRefresh}>Refresh Profile</button>;
}Error Handling
The SDK provides an ApiError class for structured error handling:
import { ApiError, isApiError } from "@fluid-app/portal-sdk";
try {
await someApiCall();
} catch (error) {
if (isApiError(error)) {
console.log("Status:", error.status);
console.log("Message:", error.message);
console.log("Data:", error.data); // Server error details
}
}License
Private - Fluid Commerce
