Vue Integration
Torrin integrates seamlessly with Vue 3 applications using the Composition API. This guide shows you how to build 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 Composition API:
vue
<script setup lang="ts">
import { ref } from 'vue';
import { createTorrinClient } from '@torrin-kit/client';
const torrin = createTorrinClient({
endpoint: 'http://localhost:3000/api/uploads',
});
const progress = ref(0);
const status = ref('idle');
const handleFileChange = async (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.value = p.percentage;
});
upload.on('status', (s) => {
status.value = s;
});
try {
const result = await upload.start();
console.log('Upload complete:', result.location);
} catch (error) {
console.error('Upload failed:', error);
}
};
</script>
<template>
<div class="space-y-4">
<input
type="file"
@change="handleFileChange"
class="block w-full text-sm"
/>
<div v-if="status !== 'idle'" class="space-y-2">
<div class="w-full bg-gray-200 rounded-full h-2.5">
<div
class="bg-green-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>
</div>
</template>Advanced Example
A complete uploader with pause, resume, and cancel functionality:
vue
<script setup lang="ts">
import { ref, shallowRef } from 'vue';
import { createTorrinClient, type TorrinUpload } from '@torrin-kit/client';
const torrin = createTorrinClient({
endpoint: 'http://localhost:3000/api/uploads',
});
const progress = ref(0);
const status = ref<string>('idle');
const error = ref<string | null>(null);
const upload = shallowRef<TorrinUpload | null>(null);
const handleFileChange = async (event: Event) => {
const input = event.target as HTMLInputElement;
const file = input.files?.[0];
if (!file) return;
error.value = null;
progress.value = 0;
const newUpload = torrin.createUpload({
file,
metadata: {
userId: 'user123',
timestamp: new Date().toISOString()
}
});
upload.value = newUpload;
newUpload.on('progress', (p) => {
progress.value = p.percentage;
console.log(`${p.chunksCompleted}/${p.totalChunks} chunks uploaded`);
});
newUpload.on('status', (s) => {
status.value = s;
});
newUpload.on('error', (err) => {
error.value = err.message;
});
try {
const result = await newUpload.start();
console.log('Upload complete:', result);
upload.value = null;
} catch (err: any) {
error.value = err.message;
}
};
const handlePause = () => {
upload.value?.pause();
};
const handleResume = () => {
upload.value?.resume();
};
const handleCancel = async () => {
await upload.value?.cancel();
upload.value = null;
progress.value = 0;
status.value = 'idle';
};
</script>
<template>
<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"
@change="handleFileChange"
:disabled="status === 'uploading'"
class="block w-full mb-4"
/>
<div v-if="error" class="mb-4 p-3 bg-red-50 border border-red-200 rounded">
<p class="text-sm text-red-600">{{ error }}</p>
</div>
<div v-if="status !== 'idle'" 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-green-600 h-3 rounded-full transition-all duration-300"
:style="{ width: `${progress}%` }"
/>
</div>
</div>
<div class="flex gap-2">
<button
v-if="status === 'uploading'"
@click="handlePause"
class="px-4 py-2 bg-yellow-500 text-white rounded hover:bg-yellow-600"
>
Pause
</button>
<button
v-if="status === 'paused'"
@click="handleResume"
class="px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600"
>
Resume
</button>
<button
v-if="status === 'uploading' || status === 'paused'"
@click="handleCancel"
class="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600"
>
Cancel
</button>
</div>
</div>
</div>
</template>Composable
Create a reusable composable for upload management:
typescript
// composables/useTorrinUpload.ts
import { ref, shallowRef } from 'vue';
import { createTorrinClient, type TorrinUpload } from '@torrin-kit/client';
const torrin = createTorrinClient({
endpoint: 'http://localhost:3000/api/uploads',
});
export function useTorrinUpload() {
const progress = ref(0);
const status = ref<string>('idle');
const error = ref<string | null>(null);
const upload = shallowRef<TorrinUpload | null>(null);
const startUpload = async (file: File, metadata?: Record<string, any>) => {
error.value = null;
progress.value = 0;
const newUpload = torrin.createUpload({ file, metadata });
upload.value = newUpload;
newUpload.on('progress', (p) => {
progress.value = p.percentage;
});
newUpload.on('status', (s) => {
status.value = s;
});
newUpload.on('error', (err) => {
error.value = err.message;
});
try {
return await newUpload.start();
} catch (err: any) {
error.value = err.message;
throw err;
}
};
const pause = () => {
upload.value?.pause();
};
const resume = () => {
upload.value?.resume();
};
const cancel = async () => {
await upload.value?.cancel();
upload.value = null;
progress.value = 0;
status.value = 'idle';
};
return {
progress,
status,
error,
startUpload,
pause,
resume,
cancel,
};
}Usage:
vue
<script setup lang="ts">
import { useTorrinUpload } from './composables/useTorrinUpload';
const { progress, status, error, startUpload, pause, resume, cancel } = useTorrinUpload();
const handleFile = async (e: Event) => {
const input = e.target as HTMLInputElement;
const file = input.files?.[0];
if (file) {
try {
const result = await startUpload(file, { userId: '123' });
console.log('Done:', result);
} catch (err) {
console.error('Failed:', err);
}
}
};
</script>
<template>
<div>
<input type="file" @change="handleFile" />
<p>Progress: {{ progress }}%</p>
<p>Status: {{ status }}</p>
<p v-if="error">Error: {{ error }}</p>
<button @click="pause">Pause</button>
<button @click="resume">Resume</button>
<button @click="cancel">Cancel</button>
</div>
</template>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.
Options API
For Vue 2 or Options API users:
vue
<script lang="ts">
import { defineComponent } from 'vue';
import { createTorrinClient } from '@torrin-kit/client';
const torrin = createTorrinClient({
endpoint: 'http://localhost:3000/api/uploads',
});
export default defineComponent({
data() {
return {
progress: 0,
status: 'idle',
upload: null as any,
};
},
methods: {
async handleFileChange(event: Event) {
const input = event.target as HTMLInputElement;
const file = input.files?.[0];
if (!file) return;
this.upload = torrin.createUpload({ file });
this.upload.on('progress', (p: any) => {
this.progress = p.percentage;
});
this.upload.on('status', (s: string) => {
this.status = s;
});
try {
const result = await this.upload.start();
console.log('Done:', result);
} catch (error) {
console.error(error);
}
},
handlePause() {
this.upload?.pause();
},
handleResume() {
this.upload?.resume();
},
async handleCancel() {
await this.upload?.cancel();
this.progress = 0;
this.status = 'idle';
},
},
});
</script>
<template>
<div>
<input type="file" @change="handleFileChange" />
<div v-if="status !== 'idle'">
<progress :value="progress" max="100"></progress>
<p>{{ progress }}% - {{ status }}</p>
<button @click="handlePause">Pause</button>
<button @click="handleResume">Resume</button>
<button @click="handleCancel">Cancel</button>
</div>
</div>
</template>Next Steps
- React Integration - React examples
- Svelte Integration - Svelte examples
- Configuration - Customize chunk size, concurrency, etc.
- Error Handling - Handle errors gracefully