StorageProviderPicker
Live preview
Storage backend
Pinata
Hosted IPFS pinning. Free signup. Simplest setup — one JWT env var.
createPinataStorage({ jwt: process.env.PINATA_JWT! })import { StorageProviderPicker } from "@cdr-kit/forms";
import { useState } from "react";
export function StorageSetup() {
const [provider, setProvider] = useState("pinata");
return <StorageProviderPicker value={provider} onChange={setProvider} />;
}A horizontal chip-pill row over a brand-colored detail card. Click any provider to see its name,
description, and the exact @cdr-kit/core factory call that builds the matching adapter.
Where to use it
This component is an admin/setup affordance. Render it in your dashboard, an onboarding wizard, or a settings page — wherever you decide which backend your platform stores form submissions in. Do not render it on the public form your respondents fill out: they should never see (or pick) the storage backend.
The picker's value (a StorageProviderId) maps 1:1 to one of the 8 Create*Storage factories shipped
by @cdr-kit/core. Your server route reads it (typically from your DB, where you persist the choice)
and constructs the matching adapter:
// app/api/respond/route.ts
import { storeFormSubmission } from "@cdr-kit/forms/server";
import {
createPinataStorage,
createSupabaseStorage,
createS3Storage,
// ... etc
} from "@cdr-kit/core";
import type { StorageProviderId } from "@cdr-kit/forms";
const choice = await db.getPlatformStorageProvider() as StorageProviderId;
const storage =
choice === "pinata" ? createPinataStorage({ jwt: process.env.PINATA_JWT! }) :
choice === "supabase" ? createSupabaseStorage({ supabaseUrl: ..., key: ..., bucket: "cdr" }) :
choice === "s3" ? createS3Storage({ bucket: ..., region: ..., accessKeyId: ..., secretAccessKey: ... }) :
/* ... */ null;
const { vaultId, cid } = await storeFormSubmission(fields, {
privateKey: process.env.PLATFORM_WALLET_PRIVATE_KEY as `0x${string}`,
storage,
});Props
| Prop | Type | Default | Description |
|---|---|---|---|
| value | StorageProviderId | undefined | — | Currently-selected provider id. Pass undefined for "none chosen". |
| onChangerequired | (id: StorageProviderId) => void | — | Fires when the user clicks a chip. |
| include | readonly StorageProviderId[] | — | Restrict + order the visible providers. Default: all 8. |
| heading | ReactNode | null | "Storage backend" | Visible eyebrow heading above the chips. Pass null to hide. |
| showDetail | boolean | true | Show the brand-colored detail card under the chips for the selected provider. Set false for a compact toolbar-only mode. |
| className | string | — | Forwarded to the root container. |
| style | CSSProperties | — | Forwarded inline style. |
The StorageProviderId union
| Prop | Type | Default | Description |
|---|---|---|---|
| pinata | StorageProviderId | — | Hosted IPFS pinning. Brand mark by cdr-kit (red). |
| supabase | StorageProviderId | — | Postgres + Object Storage. Real CC0 brand mark (green). |
| storacha | StorageProviderId | — | UCAN pinning / Filecoin. cdr-kit mark (amber). |
| ipfs | StorageProviderId | — | Self-hosted Kubo / generic. Real CC0 brand mark (teal). |
| s3 | StorageProviderId | — | AWS S3 / Cloudflare R2 / S3-compatible. cdr-kit bucket mark (orange). |
| helia | StorageProviderId | — | Browser-embedded IPFS via @helia/unixfs. cdr-kit mark (green). |
| gateway | StorageProviderId | — | Read-only IPFS gateway. cdr-kit mark (violet). |
| memory | StorageProviderId | — | In-process for tests + CI. cdr-kit mark (slate). |
Subset + ordering
If your platform only supports a couple of backends, pass include to constrain — the picker honours the
order of the array:
<StorageProviderPicker
value={value}
onChange={onChange}
include={["pinata", "supabase", "s3"]}
/>Hide the detail card
For a compact toolbar mode, set showDetail={false}:
<StorageProviderPicker value={value} onChange={onChange} showDetail={false} />