How to Add Filestack to a Vue 3 App Using filestack-js

Posted on
filestack vue

If you’ve looked at Filestack’s official SDKs, you’ve probably noticed there’s a React wrapper, an Angular wrapper, but nothing dedicated for Vue 3. That can feel like a blocker, but it really isn’t.

Filestack’s core JavaScript library, filestack-js, works in any JavaScript app. Vue 3 included. The framework wrappers are just thin layers on top of it. Once you understand that, integrating Filestack into Vue 3 takes about ten minutes.

This guide walks through the full setup using the Composition API, including how to wire up the Filestack picker, handle the upload response, and display the uploaded file in your app.

Why Use filestack-js Directly Instead of a Vue Wrapper

Quick context for anyone weighing the approach.

Filestack’s React and Angular wrappers handle a few framework-specific concerns: lifecycle integration, prop binding, and a component you can drop into your tree. They’re convenient, but they’re not the source of truth.

The source of truth is filestack-js, the official JavaScript SDK that all the wrappers depend on. For a broader walkthrough of integrating Filestack with JavaScript, this guide explains how the SDK, picker, and upload options work outside framework-specific wrappers.

For Vue 3, going straight to filestack-js gives you three things:

  • One dependency to manage instead of two
  • Full access to every picker option, transformation, and storage setting
  • A clear mental model of what’s happening under the hood

The tradeoff is that you write a small wrapper component yourself. That component is short. We’ll build it together below.

What You’ll Need Before Starting

Before any code, make sure you have:

  • A Vue 3 project (this guide uses Vite, but the setup works in any Vue 3 build)
  • Node 18 or newer
  • A free Filestack API key

If you’re starting fresh, spin up a new project in one command:

npm create vite@latest my-filestack-app — –template vue

cd my-filestack-app

npm install

That gives you a working Vue 3 app with Vite, ready for the next step.

Step 1: Install filestack-js

From your project root, run:

npm install filestack-js

That’s the entire dependency list. No additional Vue plugin needed.

Step 2: Store Your API Key in an Environment Variable

Vite reads environment variables from a .env file at the project root. Create one if it doesn’t exist:

# .env

VITE_FILESTACK_API_KEY=YOUR_API_KEY_HERE

A few notes on this. The VITE_ prefix is required for Vite to expose the variable to the client. Anything without that prefix stays server-side. Also worth saying upfront: Filestack API keys are designed to be public. They identify your application, not your account. For production apps, pair them with Filestack security policies so requests are signed and scoped.

For local development, the API key alone is enough.

Step 3: Create a Composable for the Filestack Client

Composables are how Vue 3 encourages you to share stateful logic between components. We’ll wrap the Filestack client in one.

Create a new file at src/composables/useFilestack.js:

import { ref } from 'vue'

import * as filestack from 'filestack-js'




const client = filestack.init(import.meta.env.VITE_FILESTACK_API_KEY)




export function useFilestack() {

  const uploadedFile = ref(null)

  const error = ref(null)

  const isUploading = ref(false)




  const openPicker = (options = {}) => {

    const pickerOptions = {

      maxFiles: 1,

      accept: ['image/*'],

      onUploadStarted: () => {

        isUploading.value = true

        error.value = null

      },

      onUploadDone: (res) => {

        isUploading.value = false

        if (res.filesUploaded.length > 0) {

          uploadedFile.value = res.filesUploaded[0]

        }

      },

      onFileUploadFailed: (file, err) => {

        isUploading.value = false

        error.value = err

      },

      ...options,

    }




    client.picker(pickerOptions).open()

  }




  return {

    uploadedFile,

    error,

    isUploading,

    openPicker,

  }

}

A few things worth pointing out here. The client is initialized once at module load, not inside the composable. That way every component using useFilestack shares the same client instance. The ref values are scoped to each component call, so two components using this composable get their own state. And the spread of …options at the bottom means any caller can override defaults without you having to expose every option as a separate parameter.

Step 4: Build the Upload Component

Now create a component that uses the composable. Add src/components/FileUploader.vue:

<script setup>

import { useFilestack } from '../composables/useFilestack'




const { uploadedFile, error, isUploading, openPicker } = useFilestack()




const handleUpload = () => {

  openPicker({

    maxFiles: 1,

    accept: ['image/*', 'application/pdf'],

    fromSources: ['local_file_system', 'googledrive', 'url'],

  })

}

</script>




<template>

  <div class="uploader">

    <button

      class="upload-btn"

      :disabled="isUploading"

      @click="handleUpload"

    >

      {{ isUploading ? 'Uploading...' : 'Upload a File' }}

    </button>




    <div v-if="error" class="error">

      Upload failed: {{ error.message }}

    </div>




    <div v-if="uploadedFile" class="result">

      <p><strong>File uploaded</strong></p>

      <p>Filename: {{ uploadedFile.filename }}</p>

      <p>Size: {{ uploadedFile.size }} bytes</p>

      <p>

        URL:

        <a :href="uploadedFile.url" target="_blank">

          {{ uploadedFile.url }}

        </a>

      </p>

      <img

        v-if="uploadedFile.mimetype.startsWith('image/')"

        :src="uploadedFile.url"

        :alt="uploadedFile.filename"

        class="preview"

      />

    </div>

  </div>

</template>




<style scoped>

.uploader {

  max-width: 480px;

  margin: 2rem auto;

  font-family: system-ui, sans-serif;

}




.upload-btn {

  background: #ef4a25;

  color: white;

  border: none;

  padding: 0.75rem 1.5rem;

  font-size: 1rem;

  border-radius: 6px;

  cursor: pointer;

}




.upload-btn:disabled {

  opacity: 0.6;

  cursor: not-allowed;

}




.error {

  margin-top: 1rem;

  color: #b91c1c;

}




.result {

  margin-top: 1.5rem;

  padding: 1rem;

  background: #f8fafc;

  border-radius: 6px;

}




.preview {

  max-width: 100%;

  margin-top: 1rem;

  border-radius: 6px;

}

</style>

The component does a few things. It calls useFilestack to get reactive state and the openPicker function. It passes its own picker options when the button is clicked, which override the defaults in the composable. And it renders the upload result, including a preview if the uploaded file is an image.

filestack demo

Step 5: Mount the Component

Open src/App.vue and replace the contents with:

<script setup>

import FileUploader from './components/FileUploader.vue'

</script>




<template>

  <main>

    <h1>Filestack + Vue 3 Demo</h1>

    <FileUploader />

  </main>

</template>




<style>

main {

  max-width: 720px;

  margin: 0 auto;

  padding: 2rem;

  text-align: center;

}

</style>

Run the dev server:

npm run dev

Open the URL Vite prints (usually http://localhost:5173), click the upload button, and the Filestack picker opens.

file picker

Step 6: Inspect the Upload Response

When a file finishes uploading, Filestack hands back a metadata object that looks like this:

{

  "filename": "screenshot.png",

  "handle": "apikey",

  "mimetype": "image/png",

  "originalPath": "screenshot.png",

  "size": 184320,

  "source": "local_file_system",

  "url": "https://cdn.filestackcontent.com/handle",

  "uploadId": "cfcc198e63b7328c17f09f1af519fcdf",

  "status": "Stored"

}

The two fields you’ll use most often are handle and url. The handle is the unique identifier you use for transformations, deletions, and any further API calls. The URL is the CDN-served version of the file, ready to embed in your app.

Customizing the Picker for Your Use Case

The picker accepts a long list of options. A few you’ll reach for often.

Accept Specific File Types

Restrict what users can upload by MIME type or extension:

openPicker({

  accept: ['image/jpeg', 'image/png', '.pdf'],

})

Add Cloud Sources

By default, the picker shows the local file system. You can extend it with cloud providers:

openPicker({

  fromSources: [

    'local_file_system',

    'googledrive',

    'dropbox',

    'url',

  ],

})

Apply Transformations Before Upload

Want users to crop or rotate images before they hit your storage? Add a transformations block:

openPicker({

  transformations: {

    crop: {

      aspectRatio: 16 / 9,

      force: true,

    },

    rotate: true,

  },

})

Upload Multiple Files

Set maxFiles to anything above 1 and the picker handles batch uploads. The onUploadDone callback returns an array in filesUploaded, so make sure your component handles more than one result.

openPicker({

  maxFiles: 10,

})

The full options reference is in the Filestack picker documentation, and every option you see there works the same way through the filestack-js package.

Handling Errors and Edge Cases

A few things worth wiring up before this hits production.

Per-file failures

The onFileUploadFailed callback fires when a single file fails inside a batch. Other files in the same upload can still succeed. The composable above already captures this, but in a multi-file workflow you’ll want to track failures per-file rather than overwriting a single error state.

Network interruptions

Filestack retries automatically on transient failures. If a user closes the browser mid-upload, the upload is lost. For mission-critical workflows, look at resumable uploads, which the SDK supports through the client.upload method directly.

Security

For production, generate a security policy and signature on your backend, then pass them when initializing the client:

const client = filestack.init(API_KEY, {

  security: {

    policy: 'YOUR_POLICY',

    signature: 'YOUR_SIGNATURE',

  },

})

This locks down what the API key can do and prevents abuse. The policy and signature should always be generated server-side, never in the browser.

Wrapping Up

That’s the full integration. A composable, a component, and the official filestack-js SDK. No Vue-specific wrapper required.

The pattern works the same way whether you’re building a Vue 3 app, a Nuxt 3 site, or any other JavaScript framework where an official wrapper isn’t available. Initialize the client once, expose what you need through a composable or hook, and let your components stay focused on the UI.

If you’re ready to ship, get started with Filestack and drop the code above into your project.

Related reading:

 

Read More →