@shyguy_studio/shipkit-react
v0.1.0
Published
Embed App Store Connect management in your React app. Status tracker, metadata editor, review submission.
Maintainers
Readme
@shyguy_studio/shipkit-react
Embed App Store Connect management in your React app. Pipeline tracker, app list, metadata editor, review submission — all as drop-in components.
Install
npm install @shyguy_studio/shipkit-reactQuick Start — Full Dashboard
Drop in a complete ASC dashboard with one component:
import { ShipKitDashboard } from "@shyguy_studio/shipkit-react";
function AdminPage() {
return <ShipKitDashboard proxyUrl="/api/shipkit/proxy" />;
}This renders a connect form → app list with pipeline trackers → expandable details. Everything self-contained.
Individual Components
Use components individually for custom layouts:
import { ShipKitProvider, ConnectForm, AppList, PipelineTracker, useShipKit } from "@shyguy_studio/shipkit-react";
function App() {
return (
<ShipKitProvider proxyUrl="/api/shipkit/proxy">
<ConnectForm />
<AppList onSelect={(app) => console.log(app)} />
</ShipKitProvider>
);
}PipelineTracker
Standalone pizza tracker for any app state:
<PipelineTracker state="WAITING_FOR_REVIEW" />
<PipelineTracker state="IN_REVIEW" />
<PipelineTracker state="READY_FOR_SALE" />
<PipelineTracker state="REJECTED" />useShipKit Hook
Access the client and state directly:
function MyComponent() {
const { client, apps, connected } = useShipKit();
async function pushMetadata() {
if (!client) return;
const versions = await client.getVersions(apps[0].id);
await client.pushMetadata(versions[0].id, "en-US", {
whatsNew: "Bug fixes",
});
}
async function submitForReview() {
if (!client) return;
const versions = await client.getVersions(apps[0].id);
await client.submitForReview(apps[0].id, versions[0].id);
}
}Proxy Setup (Required)
Apple's API doesn't support CORS. You need a server-side proxy. Copy one of these into your project:
Next.js (App Router)
Save as app/api/shipkit/proxy/route.ts:
import { NextRequest, NextResponse } from "next/server";
import { SignJWT, importPKCS8 } from "jose";
const ASC = "https://api.appstoreconnect.apple.com/v1";
export async function POST(request: NextRequest) {
const { keyId, issuerId, keyPem, path, params, method, body } = await request.json();
const key = await importPKCS8(keyPem, "ES256");
const now = Math.floor(Date.now() / 1000);
const jwt = await new SignJWT({})
.setProtectedHeader({ alg: "ES256", kid: keyId, typ: "JWT" })
.setIssuer(issuerId).setIssuedAt(now).setExpirationTime(now + 1200)
.setAudience("appstoreconnect-v1").sign(key);
const url = new URL(ASC + path);
if (params) Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v as string));
const res = await fetch(url.toString(), {
method: method || "GET",
headers: { Authorization: "Bearer " + jwt, "Content-Type": "application/json" },
body: body ? JSON.stringify(body) : undefined,
});
return NextResponse.json(await res.json(), { status: res.status });
}Then install jose: npm install jose
Or use the hosted proxy
For testing, you can use https://shyguy.studio/api/shipkit/proxy — but for production, self-host the proxy.
Components
| Component | Description |
|-----------|-------------|
| ShipKitDashboard | Full drop-in dashboard (connect + app list + trackers) |
| ShipKitProvider | Context provider — wrap your app |
| ConnectForm | ASC API key input form |
| AppList | List of all apps with pipeline trackers |
| AppCard | Single app card with version + tracker |
| PipelineTracker | Standalone pipeline visualization |
| useShipKit | Hook for client, apps, loading state |
License
MIT — Built by Shy Guy Studio
