Flag of Ukraine   We stand with the brave people of Ukraine. Stop the war. Find out how you can help.

Transloadit Example

Transloadit is the inventor of tus.io and Uppy. Besides a robust encoding platform, Transloadit offers hosted versions of tusd and Companion. This means you can enjoy video encoding, watermarking, face detection, resumable file uploads, fetching from Instagram, etc — all without running a single server yourself.

This example demonstrates how to unlock Transloadit’s features within Uppy.

In this particular example we take your images (from Instagram), resize them to 250px and add the copyright caption text: © Transloadit.com at the bottom right corner.

For this demo to work you'll need a (free) Transloadit account. To get one:

  1. Claim your account. It will activate instantly. You can also signup via GitHub to avoid password hassle.
  2. Copy the API Key that you can find on this page and paste it below.
  3. Optionally, copy the secret key and paste it below. This will enable Signature Authentication. Make sure nobody's watching!
  4. Happy encoding & fetching from Instagram :)

Results will appear here:


Console output:


On this page we're using the following JavaScript:

import Uppy from '@uppy/core'
import Dashboard from '@uppy/dashboard'
import Webcam from '@uppy/webcam'
import Transloadit, { COMPANION_ALLOWED_HOSTS, COMPANION_URL } from '@uppy/transloadit'
import Instagram from '@uppy/instagram'
import Facebook from '@uppy/facebook'
import Zoom from '@uppy/zoom'

const enc = new TextEncoder('utf-8')
async  function sha1 (secret, body) {
  const algorithm = { name: 'HMAC', hash: 'SHA-1' }

  const key = await crypto.subtle.importKey('raw', enc.encode(secret), algorithm, false, ['sign', 'verify'])
  const signature = await crypto.subtle.sign(algorithm.name, key, enc.encode(body))
  return Array.from(new Uint8Array(signature), x => x.toString(16).padStart(2, '0')).join('')
}

function initUppy (opts = {}) {
  if (window.uppy) {
    window.uppy.close()
  }

  const zoomMode = document.location.hash === '#enable-zoom'
  const allowedFileTypes = zoomMode ? ['video/*'] : ['image/*']
  const uppy = new Uppy({
    debug: true,
    autoProceed: false,
    restrictions: {
      maxFileSize: 1024 * 1024 * 1024,
      maxNumberOfFiles: 2,
      minNumberOfFiles: 1,
      allowedFileTypes,
    },
    locale: {
      strings: {
        youCanOnlyUploadFileTypes: 'You can only upload images',
      },
    },
  })

  function getExpiration (future) {
    return new Date(Date.now() + future)
      .toISOString()
      .replace('T', ' ')
      .replace(/\.\d+Z$/, '+00:00')
  }

  async function getAssemblyOptions () {
    const hasSecret = opts.secret != null
    let params = {
      auth: {
        key: window.TRANSLOADIT_API_KEY,
        expires: hasSecret ? getExpiration(5 * 60 * 1000) : undefined,
      },
      // It's more secure to use a template_id and enable
      // Signature Authentication
      steps: {
        resize: {
          robot: '/image/resize',
          width: 250,
          height: 250,
          resize_strategy: 'fit',
          text: [
            {
              text: `© ${(new Date()).getFullYear()} Transloadit.com`,
              size: 12,
              font: 'Ubuntu',
              color: '#eeeeee',
              valign: 'bottom',
              align: 'right',
              x_offset: 16,
              y_offset: -10,
            },
          ],
        },
      },
    }

    if (zoomMode) {
      params.steps = {
        resized: {
          use: ':original',
          robot: '/video/encode',
          result: true,
          ffmpeg_stack: 'v3.3.3',
          preset: 'ipad-high',
          resize_strategy: 'fillcrop',
        },
        watermarked: {
          use: 'resized',
          robot: '/video/encode',
          result: true,
          ffmpeg_stack: 'v3.3.3',
          preset: 'ipad-high',
          watermark_opacity: 0.7,
          watermark_position: 'top-right',
          watermark_size: '25%',
          watermark_url: 'https://demos.transloadit.com/inputs/transloadit-padded.png',
          watermark_x_offset: -10,
          watermark_y_offset: 10,
        },
      }
    }

    let signature
    if (opts.secret) {
      params = JSON.stringify(params)
      signature = await sha1(opts.secret, params)
    }

    return { params, signature }
  }

  uppy
    .use(Transloadit, {
      getAssemblyOptions,
      waitForEncoding: true,
    })
    .use(Dashboard, {
      inline: true,
      maxHeight: 400,
      target: '#uppy-dashboard-container',
      note: 'Images only, 1–2 files, up to 1 MB',
    })
    .use(Instagram, {
      target: Dashboard,
      companionUrl: COMPANION_URL,
      companionAllowedHosts: COMPANION_ALLOWED_HOSTS,
    })
    .use(Facebook, {
      target: Dashboard,
      companionUrl: COMPANION_URL,
    })
    .use(Webcam, { target: Dashboard, modes: ['picture'] })

  if (zoomMode) {
    uppy.use(Zoom, {
      target: Dashboard,
      companionUrl: COMPANION_URL,
      companionAllowedHosts: COMPANION_ALLOWED_HOSTS,
    })
  }

  uppy
    .on('transloadit:result', (stepName, result) => {
      const file = uppy.getFile(result.localId)
      const resultContainer = document.createElement('div')
      if (!zoomMode) {
        resultContainer.innerHTML = `
          <div>
            <h3>Name: ${file.name}</h3>
            <img src="${result.ssl_url}" /> <br />
            <a href="${result.ssl_url}" target="_blank">View</a>
          </div>
        `
      }

      if (zoomMode && stepName === 'watermarked') {
        resultContainer.innerHTML = `
          <div>
            <h3>Name: ${file.name}</h3>
            <video controls>
              <source src="${result.ssl_url}">
            </video>
             <br />
            <a href="${result.ssl_url}" target="_blank">View</a>
          </div>
        `
      }
      document
        .getElementById('uppy-transloadit-result')
        .appendChild(resultContainer)
    })
}

window.initUppy = initUppy

Please see documentation for details.


Hey there stranger! Uppy is actively developed and the example section is our playground. Things might not work, but we're working hard to improve.

We're on a monthly release cycle and our latest version is v3.3.1, but the example pages reflect the latest work in our main branch. Here's what changed in main since v3.3.1 in terms of commits, while the CHANGELOG provides a higher level view of the things planned for our next release.

Files from the examples are uploaded to our test servers and deleted every 24-72 hours.