Flag of Ukraine

My first App

Transloadit is versatile and there are many ways to leverage our API. To pick one happy path for demo purposes, weʼll default to a web browser integration with Uppyʼs Transloadit Plugin.

We will create a simple web page where the following happens:

  1. You can upload a photo (handled by Transloadit via 🤖/upload/handle).
  2. Transloadit will then automatically resize and crop the image to 1500x500 (via 🤖/image/resize).
  3. The result is stored in your S3 bucket (via 🤖/s3/store).

Let's look at the Assembly Instructions to accomplish this. We'll see three Steps that we named: :original, crop_thumbed, and exported:

  "steps": {
    ":original": {
      "robot": "/upload/handle"
    "crop_thumbed": {
      "use": ":original",
      "robot": "/image/resize",
      "result": true,
      "width": 1500,
      "height": 500,
      "resize_strategy": "fillcrop",
      "imagemagick_stack": "v3.0.0"
    "exported": {
      "use": ["crop_thumbed", ":original"],
      "robot": "/s3/store",
      "credentials": "YOUR_AWS_CREDENTIALS",
      "url_prefix": "https://demos.transloadit.com/"

Note: The credentials to S3 are stored separately for security purposes. You can read more about this in the Template Credentials docs. You refer to them by name, replacing YOUR_AWS_CREDENTIALS.

Now, let's take a closer look at those three Steps:

  1. :original invokes 🤖/upload/handle, which will receive any file Uppy throws at it, and then make it available for other Robots to consume.
  2. crop_thumbed is happy to take those files off :original's hands. It explicitly says "use": ":original" to indicate that. It also lists "robot": "/image/resize" to invoke the image resize, along with some parameters that are specific to 🤖/image/resize, such as resize_strategy.
  3. exported takes files emitted by both :original and crop_thumbed, and then invokes 🤖/s3/store to export them to an S3 bucket. This way, we'll have saved both the original upload, and the cropped image, to S3.

If you don't have an S3 bucket (or any other supported export target), you could create one or leave out the exported Step for now. Our prototype will still work, but result files are then hosted with us and removed after 24h.

We save these JSON Instructions in a Template in the Template Editor in your account. That way, we can get a Template ID, which we can then refer to in our Uppy integration (currently indicated by YOUR_TEMPLATE_ID). We'll add this to any HTML page that is accessible in your browser, which could be just a local test.html for now:

<!-- This pulls Uppy from our CDN -->
<!-- For smaller self-hosted bundles, install Uppy and plugins manually: -->
<!-- npm i --save @uppy/core @uppy/dashboard @uppy/remote-sources @uppy/transloadit ... -->
<button id="browse">Select Files</button>
<script type="module">
  import {
  } from 'https://releases.transloadit.com/uppy/v3.22.2/uppy.min.mjs'
  const uppy = new Uppy()
    .use(Transloadit, {
      waitForEncoding: true,
      alwaysRunAssembly: true,
      assemblyOptions: {
        params: {
          template_id: 'YOUR_TEMPLATE_ID',
          // To avoid tampering, use Signature Authentication:
          // https://transloadit.com/docs/topics/signature-authentication/
          auth: {
            key: 'YOUR_TRANSLOADIT_KEY',
    .use(Dashboard, { trigger: '#browse' })
    .use(ImageEditor, { target: Dashboard })
    .use(RemoteSources, {
      companionUrl: 'https://api2.transloadit.com/companion',
    .on('complete', ({ transloadit }) => {
      // Due to waitForEncoding:true this is fired after encoding is done.
      // Alternatively, set waitForEncoding to false and provide a notify_url
      console.log(transloadit) // Array of Assembly Statuses
      transloadit.forEach((assembly) => {
        console.log(assembly.results) // Array of all encoding results
    .on('error', (error) => {

You'll notice that along with YOUR_TEMPLATE_ID, we'll also need to replace YOUR_TRANSLOADIT_KEY, the value for which can be obtained from the API Settings in your Account.

And there you have it! ✨ If you were copy/pasting/replacing along, you now have a working prototype. The results are dumped in your browser's console log, but you can also see Assemblies added to your account in real time. If you didn't type along, you can still try it live in this image resize demo.

We're not quite there yet, though. The results are in your S3 bucket and referenced in the console log. But how to get these results to your back-end so that others can enjoy them too? Find out on the Saving Results page.