Skip to content

Commit

Permalink
Update 2024-07-10-uppy-4.0.md
Browse files Browse the repository at this point in the history
My iteration, plus addressing Evgenia's feedback.
  • Loading branch information
AJvanLoon authored Jul 15, 2024
1 parent 2da156b commit 87526dd
Showing 1 changed file with 71 additions and 65 deletions.
136 changes: 71 additions & 65 deletions blog/2024-07-10-uppy-4.0.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title:
'New Uppy 4.0 major: TypeScript rewrite, Google Photos, React hooks, and much
more.'
more'
date: 2024-07-10
authors: [aduh95, evgenia, mifi, murderlon]
image: /img/blog/4.0/preview.jpg
Expand All @@ -10,30 +10,30 @@ published: true
toc_max_heading_level: 2
---

<img src="/img/blog/4.0/preview.jpg" alt="Screenshot of Uppy Dashboad with text outlining the major new features in 4.0" />
<img src="/img/blog/4.0/preview.jpg" alt="Screenshot of Uppy Dashboard with text outlining the major new features in 4.0" />

Hold on to your leashes, folks! Uppy 4.0 is here, and it’s more exciting than a
tennis ball at the dog park! Our beloved Uppy mascot, the adorable coding
canine, has been hard at work fetching all the latest updates for you.

From a full TypeScript makeover to shiny new React hooks, and even Google Photos
integration, this release is packed with treats that will make developers wag
their tails in delight. So, sit, stay, and let’s dig into what makes Uppy 4.0
the goodest of good boys in file uploading!
integrationthis release is so packed with treats that we're almost wagging
our tails in excitement. Without further a-dog, let’s dig into what makes Uppy
4.0 the goodest of good boys in file uploading!

<!--truncate-->

## Migration guide

This post goes into the most exciting new features of Uppy 4.0. We have an
This post covers the most exciting new features of Uppy 4.0. We have an
accompanying [migration guide](/docs/guides/migration-guides/) to help you
transition to 4.0.

## TypeScript rewrite

In the year 2024 people expect excellent types from their libraries. We used to
author types separately by hand but they were often inconsistent or incomplete.
Now Uppy has been completely rewritten in TypeScript!
In the year 2024, people expect excellent types from their libraries. We used to
author types separately by hand, but they were often inconsistent or incomplete.
As of now, Uppy has been completely rewritten in TypeScript!

From now on you’ll be in safe hands when working with Uppy, whether it’s setting
the right options, building plugins, or listening to events.
Expand All @@ -49,13 +49,13 @@ uppy.on('file-added', (file) => {
});
```

One important thing to note are the new generics on `@uppy/core`.
One important thing to note is the new generics on `@uppy/core`.

<!-- eslint-disable @typescript-eslint/no-non-null-assertion -->

```ts
import Uppy from '@uppy/core';
// xhr-upload is for uploading to your own backend.
// xhr-upload is for uploading to your own back end.
import XHRUpload from '@uppy/xhr-upload';

// Your own metadata on files
Expand Down Expand Up @@ -86,8 +86,8 @@ Happy inferring!

## Merging the two AWS S3 plugins

We used to have two separate plugins for uploading to S3 (and S3-compatible
services): `@uppy/aws-s3` and `@uppy/aws-s3-multpart`. They have different use
We used to have two separate plugins for uploading to S3 and S3-compatible
services: `@uppy/aws-s3` and `@uppy/aws-s3-multpart`. They have different use
cases. The advantages of multipart uploads are:

- Improved throughput – You can upload parts in parallel to improve throughput.
Expand All @@ -105,33 +105,33 @@ uploading files that are only a couple kilobytes with a 100ms roundtrip latency,
you are spending 400ms on overhead and only a few milliseconds on uploading.
This really adds up if you upload a lot of small files.

AWS, and generally the internet from what we found, tend to agree that **you
don’t want to use multipart uploads for files under 100MB**. But this sometimes
puts users of our libraries in an awkward position, as their end users may not
only upload very large files, or only small files. In this case a portion of
their users get a subpar experience.
AWSand the internet in general, from what we found – tends to agree that
**you don’t want to use multipart uploads for files under 100 MB**. This
sometimes puts users of our libraries in an awkward position, though, as their
end users may not exclusively upload very large files, or only small files.
In this case, a portion of their users get a subpar experience.

---

We’ve merged the two plugins into `@uppy/aws-s3` with a new
[`shouldUseMultipart`](/docs/aws-s3/#shouldusemultipartfile) option! By default
it switches to multipart uploads if the file is larger than 100MB. You can pass
a `boolean` or a function to determine it per file.
[`shouldUseMultipart`](/docs/aws-s3/#shouldusemultipartfile) option! By default,
it switches to multipart uploads if the file is larger than 100 MB. You can pass
a `boolean` or a function to determine this per file.

## React hooks

People working with React are more likely to create their own user interface on
top of Uppy than those working with “vanilla” setups. Working with our pre-build
top of Uppy than those working with “vanilla” setups. Working with our pre-built
UI components is a plug-and-play experience, but building on top of Uppy’s state
with React primitives has been tedious.

To address this we’re introducing to new hooks: `useUppyState` and
To address this, we’re introducing two new hooks: `useUppyState` and
`useUppyEvent`. Thanks to the TypeScript rewrite, we can now do powerful
inference in hooks as well.

### `useUppyState(uppy, selector)`

Use this hook when you need to access Uppy’s state reactively.
Use this hook when you need to read Uppy’s state.

```ts
import { useState } from 'react';
Expand All @@ -144,7 +144,7 @@ const [uppy] = useState(() => new Uppy());

const files = useUppyState(uppy, (state) => state.files);
const totalProgress = useUppyState(uppy, (state) => state.totalProgress);
// We can also get specific plugin state.
// We can also get a specific plugin state.
// Note that the value on `plugins` depends on the `id` of the plugin.
const metaFields = useUppyState(
uppy,
Expand All @@ -154,8 +154,11 @@ const metaFields = useUppyState(

You can see all the values you can access on the
[`State`](https://github.com/transloadit/uppy/blob/dab8082a4e67c3e7f109eacfbd6c3185f117dc60/packages/%40uppy/core/src/Uppy.ts#L156)
type. If you are accessing plugin state, you would have to look at the types of
the plugin.
type.

Using this hook, you can also access the state of any Uppy plugin. For example,
in order to access the state of `ImageEditor`, you would have to look at the
types of the plugin.

```ts
import type { State } from '@uppy/core';
Expand All @@ -166,12 +169,12 @@ import type { State } from '@uppy/core';
Listen to Uppy [events](/docs/uppy/#events) in a React component.

The hook returns `[results, clear]`. `results` is an array of values from the
event. Depending on the event, that can be empty or have up to three values.
event. Depending on the event, this can be empty or have up to three values.
`clear` is a function to clear the `results` array.

Values remain in state until the next event (if that ever comes) or if the state
is manually cleared. Depending on your use case, you may want to keep the values
in state or clear the state after something else happened.
Values remain in state until the next event (if that ever comes) or the moment
when the state is manually cleared. Depending on your use case, you may want
to keep the values in state or clear the state after something else happened.

```ts
import { useState } from 'react';
Expand All @@ -193,40 +196,42 @@ useUppyEvent(uppy, 'cancel-all', () => {

## Google Photos

A long requested feature is finally here: Google Photos support!
An often requested feature is finally here: Google Photos support!

:::info

Uppy can bring in files from the cloud with [Companion](/docs/companion/).

Companion is a hosted, standalone, or middleware server to take away the
Companion is a hosted, standalone, or middleware server that takes away the
complexity of authentication and the cost of downloading files from remote
sources, such as Instagram, Google Drive, and others.

This means a 5GB video isn’t eating into your users’ data plans and you don’t
have to worry about OAuth.
This means a 5 GB video isn’t eating into your users’ data plans and you
don’t have to worry about OAuth.

:::

<video src="/img/blog/4.0/google-photos.mp4" controls></video>

[`@uppy/google-photos`](/docs/google-photos/) is a new plugin so you can use it
next to your existing [`@uppy/google-drive`](/docs/google-drive/) plugin.
[`@uppy/google-drive`](/docs/google-drive/) and
[`@uppy/google-photos`](/docs/google-photos/) are separate plugins. However,
you can use the same OAuth app for both these plugins. Be sure to enable
"Photos Library API" in your OAuth app, though!

## UX improvements for viewing remote files

When using [Dashboard](/docs/dashboard) with any of our remote sources (Google
Drive, Facebook, etc.) you use our internal `@uppy/provider-views` plugin to
navigate and select files. In Uppy 4.0, we are making a handful of quality of
life improvements for users.
We describe the main changes in a table below.
navigate the folders and select files. In Uppy 4.0, we are making a few quality
of life improvements for users. The main changes are described in the table
below.

<table style="text-align:left; font-size: 15px">
<tbody>
<tr>
<td colspan="2">
<b style="font-size: 17px;">Folder states: checked, unchecked, partial</b>
<p style="margin-bottom: 0;">In 4.0, we introduce a new folder state - a "partially checked" folder. A folder acquires this state when some files within the folder are "checked", and some files are "unchecked".</p>
<p style="margin-bottom: 0;">In 4.0, we introduce a new folder state a "partially checked" folder. A folder acquires this state when certain files within the folder are "checked", and other files are "unchecked".</p>
</td>
</tr>
<tr>
Expand All @@ -240,7 +245,7 @@ We describe the main changes in a table below.
<tr>
<td colspan="2">
<b style="font-size: 17px;">Cache</b>
<p style="margin-bottom: 0;">When navigating in and out of folders, you no longer have to wait for the same API call — results get cached.</p>
<p style="margin-bottom: 0;">When navigating in and out of folders, you no longer have to wait for the same API call — results are cached.</p>
</td>
</tr>
<tr>
Expand All @@ -255,7 +260,7 @@ We describe the main changes in a table below.
<td colspan="2">
<b style="font-size: 17px;">Restrictions</b>
<p style="margin-bottom: 0;">
Uppy supports file <a href="/docs/uppy/#restrictions">restrictions</a>, such as maximum number of files and maximum file size. In 4.0, we reworked our restrictions UI - users will get immediate feedback upon exceeding the number of selected files, and users get a chance to reenter the correct number of files after their first upload attempt.
Uppy supports file <a href="/docs/uppy/#restrictions">restrictions</a>, such as maximum number of files and maximum file size. In 4.0, we reworked our restrictions UI users will get immediate feedback upon exceeding the number of selected files, and get a chance to re-enter the correct number of files after their first upload attempt.
</p>
</td>
</tr>
Expand All @@ -272,22 +277,23 @@ We describe the main changes in a table below.

We’re confident this turns our interface for remote sources into the most
advanced one out there. We’ve seen some competing libraries not even aggregating
results beyond the first page API limit of providers.
results beyond the first page returned by the provider API.

## Revamped options for `@uppy/xhr-upload`

Before the plugin had the options `getResponseData`, `getResponseError`,
`validateStatus` and `responseUrlFieldName`. These were inflexible and too
specific. Now we have hooks similar to `@uppy/tus`:
In previous versions, the `@uppy/xhr-upload` plugin had the options
`getResponseData`, `getResponseError`, `validateStatus` and
`responseUrlFieldName`. These were inflexible and too specific. Now
we have hooks similar to `@uppy/tus`:

- `onBeforeRequest` to manipulate the request before it is sent.
- `shouldRetry` to determine if a request should be retried. By default 3
- `shouldRetry` to determine if a request should be retried. By default, three
retries with exponential backoff. After three attempts it will throw an error,
regardless of whether you returned `true`.
- `onAfterResponse` called after a successful response but before Uppy resolves
- `onAfterResponse` called after a successful response, but before Uppy resolves
the upload.

You could for instance use them to refresh your auth token when it expires:
You could, for instance, use them to refresh your auth token when it expires:

```js
import Uppy from '@uppy/core';
Expand Down Expand Up @@ -318,11 +324,11 @@ new Uppy().use(XHR, {
});
```

Checkout the `@uppy/xhr-upload` [docs](/docs/xhr-upload/) for more info.
Check out the `@uppy/xhr-upload` [docs](/docs/xhr-upload/) for more info.

## Simpler configuration for `@uppy/transloadit`

To get started with `@uppy/transloadit` you would configure
To get started with `@uppy/transloadit`, you would configure
[`assemblyOptions`](/docs/transloadit/#assemblyoptions) with your auth key,
template ID, and other optional values. `assemblyOptions` can be an object or a
function, which is called per file, which returns an object:
Expand All @@ -344,7 +350,7 @@ function, which is called per file, which returns an object:
}
```

When you go to production you always want to make sure to set the `signature`.
When you go to production, you always want to make sure to set the `signature`.
**Not using
[Signature Authentication](https://transloadit.com/docs/topics/signature-authentication/)
can be a security risk**. Signature Authentication is a security measure that
Expand All @@ -365,17 +371,17 @@ new Uppy().use(Transloadit, {
});
```

But now you are making a request to your backend for _every file_, while the
response likely remains the same, unless you are setting dynamic `fields` per
file.
However, now you are making a request to your back end for _every file_, while
the response likely remains the same, unless you are setting dynamic `fields`
per file.

**This has now been improved to**:

- only call `assemblyOptions()` once.
- Only call `assemblyOptions()` once.
- `fields` is for global variables in your
[template](https://transloadit.com/docs/topics/templates/).
- all metadata on your files is automatically sent along to Transloadit. This
will end up in `file.user_meta` for you to dynamically access in your template
- All metadata on your files is automatically sent along to Transloadit. This
will end up in `file.user_meta` for you to dynamically access in your Template
_per file_.

You can read more about Assembly Variables in the
Expand All @@ -385,12 +391,12 @@ You can read more about Assembly Variables in the

### Streaming uploads by default

Streaming uploads are now the default in in Companion. This comes with greatly
Streaming uploads are now the default in Companion. This comes with greatly
improved upload speeds and allows uploading up to hundreds of gigabytes without
needing a large server storage. We found that this improves speeds by about 37%
for a Google Drive upload of a 1 GB file
([source](https://github.com/transloadit/uppy/pull/4046#issuecomment-1235697937)).
This feature was also available before but we wanted to have more real world
This feature was also available before, but we wanted to have more real-world
usage before setting it as the default.

With streaming upload disabled, the whole file will be downloaded first. The
Expand All @@ -407,14 +413,14 @@ For more information, see the [Companion docs](/docs/companion/).
As a security measure, we now require the
[`corsOrigins`](/docs/companion/#corsorigins) option to be set.

It serves two purposes, it sets the `Access-Control-Allow-Origin` header and it
sets the target origin for `window.postMessage()`, which is needed to
communicate the OAuth token from the new tab you used to log-in to a provider
It serves two purposes: it sets the `Access-Control-Allow-Origin` header as well
as the target origin for `window.postMessage()`, which is needed to
communicate the OAuth token from the new tab you used to log in to a provider
back to Companion.

## And more

The 4.0 release contained over 170 contributions, many too small to mention, but
The 4.0 release contains over 170 contributions, many too small to mention, but
together resulting in Uppy continuing to grow and improve. We closely listen to
the community and are always looking for ways to improve the experience.

Expand Down

0 comments on commit 87526dd

Please sign in to comment.