Recipes
Same upload() function, every runtime. Pick the snippet closest to your stack and adjust.
React + input
'use client';import { useState } from 'react';import { upload } from '@dropkit/sdk';
export function FileUpload() { const [status, setStatus] = useState<'idle' | 'uploading' | 'done'>('idle'); const [url, setUrl] = useState<string | null>(null);
async function onChange(e: React.ChangeEvent<HTMLInputElement>) { const file = e.target.files?.[0]; if (!file) return; setStatus('uploading'); const { data, error } = await upload(file, { key: process.env.NEXT_PUBLIC_DROPKIT_KEY!, }); if (error) { alert(error.message); setStatus('idle'); return; } setUrl(data.url); setStatus('done'); }
return ( <div> <input type="file" onChange={onChange} disabled={status === 'uploading'} /> {url && <img src={url} alt="" />} </div> );}Svelte 5
<script lang="ts"> import { upload } from '@dropkit/sdk'; import { env } from '$env/dynamic/public';
let status = $state<'idle' | 'uploading' | 'done'>('idle'); let url = $state<string | null>(null);
async function onChange(e: Event) { const file = (e.target as HTMLInputElement).files?.[0]; if (!file) return; status = 'uploading'; const { data, error } = await upload(file, { key: env.PUBLIC_DROPKIT_KEY!, }); if (error) { alert(error.message); status = 'idle'; return; } url = data.url; status = 'done'; }</script>
<input type="file" onchange={onChange} disabled={status === 'uploading'} />{#if url}<img src={url} alt="" />{/if}Vue 3 composition
<script setup lang="ts">import { ref } from 'vue';import { upload } from '@dropkit/sdk';
const status = ref<'idle' | 'uploading' | 'done'>('idle');const url = ref<string | null>(null);
async function onChange(e: Event) { const file = (e.target as HTMLInputElement).files?.[0]; if (!file) return; status.value = 'uploading'; const { data, error } = await upload(file, { key: import.meta.env.VITE_DROPKIT_KEY, }); if (error) { alert(error.message); status.value = 'idle'; return; } url.value = data.url; status.value = 'done';}</script>
<template> <input type="file" @change="onChange" :disabled="status === 'uploading'" /> <img v-if="url" :src="url" /></template>Vanilla HTML, no bundler
<input type="file" id="u" /><img id="preview" />
<script type="module"> import { upload } from 'https://esm.sh/@dropkit/sdk';
document.getElementById('u').addEventListener('change', async (e) => { const file = e.target.files[0]; const { data, error } = await upload(file, { key: 'pk_live_...' }); if (error) return alert(error.message); document.getElementById('preview').src = data.url; });</script>Next App Router server action
Use a server key from your backend so you don’t expose keys to the browser.
'use server';
export async function uploadAction(formData: FormData) { const file = formData.get('file') as File; const buf = await file.arrayBuffer();
const res = await fetch('https://api.dropkit.app/v1/upload', { method: 'POST', headers: { authorization: `Bearer ${process.env.DROPKIT_SECRET_KEY!}`, 'content-type': file.type, 'x-filename': file.name, }, body: buf, }); if (!res.ok) throw new Error(await res.text()); const { url } = (await res.json()) as { url: string }; return url;}import { uploadAction } from '@/app/actions/upload';
export default function Page() { return ( <form action={uploadAction}> <input name="file" type="file" required /> <button type="submit">Upload</button> </form> );}Remix action
import type { ActionFunctionArgs } from '@remix-run/node';
export async function action({ request }: ActionFunctionArgs) { const formData = await request.formData(); const file = formData.get('file') as File; const buf = await file.arrayBuffer();
const res = await fetch('https://api.dropkit.app/v1/upload', { method: 'POST', headers: { authorization: `Bearer ${process.env.DROPKIT_SECRET_KEY!}`, 'content-type': file.type, 'x-filename': file.name, }, body: buf, }); const { url } = await res.json(); return { url };}Expo / React Native
import * as DocumentPicker from 'expo-document-picker';import { upload } from '@dropkit/sdk';
async function pickAndUpload() { const result = await DocumentPicker.getDocumentAsync({}); if (result.canceled) return; const asset = result.assets[0];
// Expo gives a URI; turn it into a File for the SDK. const blob = await fetch(asset.uri).then((r) => r.blob()); const file = new File([blob], asset.name, { type: asset.mimeType ?? 'application/octet-stream' });
const { data, error } = await upload(file, { key: process.env.EXPO_PUBLIC_DROPKIT_KEY! }); if (error) throw new Error(error.message); return data.url;}SolidJS
import { createSignal } from 'solid-js';import { upload } from '@dropkit/sdk';
export function FileUpload() { const [url, setUrl] = createSignal<string | null>(null);
async function onChange(e: Event) { const file = (e.target as HTMLInputElement).files?.[0]; if (!file) return; const { data, error } = await upload(file, { key: import.meta.env.VITE_DROPKIT_KEY, }); if (error) return alert(error.message); setUrl(data.url); }
return ( <> <input type="file" onChange={onChange} /> {url() && <img src={url()!} />} </> );}Python server
import osimport requests
def upload(path: str) -> str: with open(path, 'rb') as f: r = requests.post( 'https://api.dropkit.app/v1/upload', headers={ 'authorization': f'Bearer {os.environ["DROPKIT_SECRET_KEY"]}', 'content-type': 'application/octet-stream', 'x-filename': os.path.basename(path), }, data=f.read(), ) r.raise_for_status() return r.json()['url']Go server
package main
import ( "bytes" "encoding/json" "io" "net/http" "os" "path/filepath")
type uploadResp struct{ URL string `json:"url"` }
func UploadFile(path string) (string, error) { data, err := os.ReadFile(path) if err != nil { return "", err }
req, _ := http.NewRequest("POST", "https://api.dropkit.app/v1/upload", bytes.NewReader(data)) req.Header.Set("authorization", "Bearer "+os.Getenv("DROPKIT_SECRET_KEY")) req.Header.Set("content-type", "application/octet-stream") req.Header.Set("x-filename", filepath.Base(path))
res, err := http.DefaultClient.Do(req) if err != nil { return "", err } defer res.Body.Close() if res.StatusCode >= 300 { body, _ := io.ReadAll(res.Body) return "", &httpError{res.StatusCode, string(body)} } var out uploadResp if err := json.NewDecoder(res.Body).Decode(&out); err != nil { return "", err } return out.URL, nil}Same pattern everywhere
Notice the shape in all browser-side snippets:
const { data, error } = await upload(file, { key });if (error) throw new Error(error.message);use(data.url);That is the whole SDK. No framework-specific components to learn. Use your own UI, your own state library, your own error handling.