Svelte Integration
Torrin works beautifully with Svelte's reactive system. This guide shows you how to build efficient upload components with progress tracking, pause/resume, and error handling.
Installation
bash
npm install @torrin-kit/clientbash
yarn add @torrin-kit/clientbash
pnpm add @torrin-kit/clientbash
bun add @torrin-kit/clientBasic Example
A simple file uploader with progress tracking using Svelte's reactivity:
svelte
<script lang="ts">
import { createTorrinClient } from '@torrin-kit/client';
const torrin = createTorrinClient({
endpoint: 'http://localhost:3000/api/uploads',
});
let progress = 0;
let status = 'idle';
async function handleFileChange(event: Event) {
const input = event.target as HTMLInputElement;
const file = input.files?.[0];
if (!file) return;
const upload = torrin.createUpload({ file });
upload.on('progress', (p) => {
progress = p.percentage;
});
upload.on('status', (s) => {
status = s;
});
try {
const result = await upload.start();
console.log('Upload complete:', result.location);
} catch (error) {
console.error('Upload failed:', error);
}
}
</script>
<div class="space-y-4">
<input
type="file"
on:change={handleFileChange}
class="block w-full text-sm"
/>
{#if status !== 'idle'}
<div class="space-y-2">
<div class="w-full bg-gray-200 rounded-full h-2.5">
<div
class="bg-orange-600 h-2.5 rounded-full transition-all"
style="width: {progress}%"
/>
</div>
<div class="flex justify-between text-sm">
<span>{status}</span>
<span>{progress.toFixed(1)}%</span>
</div>
</div>
{/if}
</div>Advanced Example
A complete uploader with pause, resume, and cancel functionality:
svelte
<script lang="ts">
import { createTorrinClient, type TorrinUpload } from '@torrin-kit/client';
const torrin = createTorrinClient({
endpoint: 'http://localhost:3000/api/uploads',
});
let progress = 0;
let status: string = 'idle';
let error: string | null = null;
let upload: TorrinUpload | null = null;
async function handleFileChange(event: Event) {
const input = event.target as HTMLInputElement;
const file = input.files?.[0];
if (!file) return;
error = null;
progress = 0;
upload = torrin.createUpload({
file,
metadata: {
userId: 'user123',
timestamp: new Date().toISOString()
}
});
upload.on('progress', (p) => {
progress = p.percentage;
console.log(`${p.chunksCompleted}/${p.totalChunks} chunks uploaded`);
});
upload.on('status', (s) => {
status = s;
});
upload.on('error', (err) => {
error = err.message;
});
try {
const result = await upload.start();
console.log('Upload complete:', result);
upload = null;
} catch (err: any) {
error = err.message;
}
}
function handlePause() {
upload?.pause();
}
function handleResume() {
upload?.resume();
}
async function handleCancel() {
await upload?.cancel();
upload = null;
progress = 0;
status = 'idle';
}
</script>
<div class="max-w-md p-6 bg-white rounded-lg shadow">
<h2 class="text-xl font-bold mb-4">Upload File</h2>
<input
type="file"
on:change={handleFileChange}
disabled={status === 'uploading'}
class="block w-full mb-4"
/>
{#if error}
<div class="mb-4 p-3 bg-red-50 border border-red-200 rounded">
<p class="text-sm text-red-600">{error}</p>
</div>
{/if}
{#if status !== 'idle'}
<div class="space-y-4">
<div>
<div class="flex justify-between text-sm mb-1">
<span class="font-medium">Status: {status}</span>
<span>{progress.toFixed(1)}%</span>
</div>
<div class="w-full bg-gray-200 rounded-full h-3">
<div
class="bg-orange-600 h-3 rounded-full transition-all duration-300"
style="width: {progress}%"
/>
</div>
</div>
<div class="flex gap-2">
{#if status === 'uploading'}
<button
on:click={handlePause}
class="px-4 py-2 bg-yellow-500 text-white rounded hover:bg-yellow-600"
>
Pause
</button>
{/if}
{#if status === 'paused'}
<button
on:click={handleResume}
class="px-4 py-2 bg-orange-500 text-white rounded hover:bg-orange-600"
>
Resume
</button>
{/if}
{#if status === 'uploading' || status === 'paused'}
<button
on:click={handleCancel}
class="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600"
>
Cancel
</button>
{/if}
</div>
</div>
{/if}
</div>Svelte Store
Create a reusable store for upload management:
typescript
// stores/torrinStore.ts
import { writable, get } from 'svelte/store';
import { createTorrinClient, type TorrinUpload } from '@torrin-kit/client';
const torrin = createTorrinClient({
endpoint: 'http://localhost:3000/api/uploads',
});
interface UploadState {
progress: number;
status: string;
error: string | null;
upload: TorrinUpload | null;
}
function createTorrinStore() {
const { subscribe, set, update } = writable<UploadState>({
progress: 0,
status: 'idle',
error: null,
upload: null,
});
return {
subscribe,
async startUpload(file: File, metadata?: Record<string, any>) {
const upload = torrin.createUpload({ file, metadata });
update(state => ({
...state,
error: null,
progress: 0,
upload,
}));
upload.on('progress', (p) => {
update(state => ({ ...state, progress: p.percentage }));
});
upload.on('status', (s) => {
update(state => ({ ...state, status: s }));
});
upload.on('error', (err) => {
update(state => ({ ...state, error: err.message }));
});
try {
return await upload.start();
} catch (err: any) {
update(state => ({ ...state, error: err.message }));
throw err;
}
},
pause() {
const state = get({ subscribe });
state.upload?.pause();
},
resume() {
const state = get({ subscribe });
state.upload?.resume();
},
async cancel() {
const state = get({ subscribe });
await state.upload?.cancel();
set({
progress: 0,
status: 'idle',
error: null,
upload: null,
});
},
};
}
export const torrinUpload = createTorrinStore();Usage:
svelte
<script lang="ts">
import { torrinUpload } from './stores/torrinStore';
async function handleFile(e: Event) {
const input = e.target as HTMLInputElement;
const file = input.files?.[0];
if (file) {
try {
const result = await torrinUpload.startUpload(file, { userId: '123' });
console.log('Done:', result);
} catch (err) {
console.error('Failed:', err);
}
}
}
</script>
<div>
<input type="file" on:change={handleFile} />
<p>Progress: {$torrinUpload.progress}%</p>
<p>Status: {$torrinUpload.status}</p>
{#if $torrinUpload.error}
<p>Error: {$torrinUpload.error}</p>
{/if}
<button on:click={() => torrinUpload.pause()}>Pause</button>
<button on:click={() => torrinUpload.resume()}>Resume</button>
<button on:click={() => torrinUpload.cancel()}>Cancel</button>
</div>With Resume Support
Enable automatic resume after page refresh:
typescript
import { createTorrinClient, createLocalStorageResumeStore } from '@torrin-kit/client';
const torrin = createTorrinClient({
endpoint: 'http://localhost:3000/api/uploads',
resumeStore: createLocalStorageResumeStore(), // Enable auto-resume
});Now if the user refreshes the page and selects the same file, the upload will automatically resume from where it left off.
SvelteKit Example
For SvelteKit applications with SSR:
svelte
<script lang="ts">
import { browser } from '$app/environment';
import { onMount } from 'svelte';
import type { TorrinClient } from '@torrin-kit/client';
let torrin: TorrinClient;
let progress = 0;
let status = 'idle';
onMount(async () => {
if (browser) {
const { createTorrinClient } = await import('@torrin-kit/client');
torrin = createTorrinClient({
endpoint: '/api/uploads',
});
}
});
async function handleFileChange(event: Event) {
if (!torrin) return;
const input = event.target as HTMLInputElement;
const file = input.files?.[0];
if (!file) return;
const upload = torrin.createUpload({ file });
upload.on('progress', (p) => {
progress = p.percentage;
});
upload.on('status', (s) => {
status = s;
});
try {
await upload.start();
console.log('Upload complete');
} catch (error) {
console.error(error);
}
}
</script>
{#if browser}
<div>
<input type="file" on:change={handleFileChange} />
{#if status !== 'idle'}
<progress value={progress} max="100" />
<p>{progress}% - {status}</p>
{/if}
</div>
{/if}TypeScript Support
Define proper types for your upload state:
typescript
import type { TorrinUpload, TorrinProgress } from '@torrin-kit/client';
interface UploadState {
progress: TorrinProgress | null;
status: 'idle' | 'uploading' | 'paused' | 'completed' | 'failed';
error: Error | null;
upload: TorrinUpload | null;
}Next Steps
- React Integration - React examples
- Vue Integration - Vue examples
- Configuration - Customize chunk size, concurrency, etc.
- Error Handling - Handle errors gracefully