Skip to content

Commit 82fa731

Browse files
committed
4.0 blog post
1 parent 4622d89 commit 82fa731

File tree

1 file changed

+238
-0
lines changed

1 file changed

+238
-0
lines changed

blog/2024-07-3-uppy-4.0.md

+238
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
---
2+
title:
3+
'New Uppy 4.0 major: TypeScript rewrite, Google Photos, React hooks, and much
4+
more.'
5+
date: 2024-07-03
6+
authors: [aduh95, evgenia, mifi, murderlon]
7+
image: 'https://uppy.io/img/blog/3.13-3.21/dog-coding-laptop-mars-christmas-tree-2.jpg'
8+
slug: 'uppy-4.0'
9+
published: false
10+
toc_max_heading_level: 2
11+
---
12+
13+
Hi ha ho, this is some goofy introduction.
14+
15+
## TypeScript rewrite
16+
17+
In the year 2024 people expect exellent types from their libaries. We used to
18+
author types separately by hand but they were often inconsistent or incomplete.
19+
Now Uppy has been completely rewritten in TypeScript!
20+
21+
From now on you’ll be in safe hands when working with Uppy, whether it’s setting
22+
the right options, building plugins, or listening to events.
23+
24+
```ts
25+
import Uppy from '@uppy/core';
26+
27+
const uppy = new Uppy();
28+
29+
// Event name autocompletion and inferred argument types
30+
uppy.on('file-added', (file) => {
31+
console.log(file);
32+
});
33+
```
34+
35+
One important thing to note are the new generics on `@uppy/core`.
36+
37+
```ts
38+
import Uppy from '@uppy/core';
39+
// xhr-upload is for uploading to your own backend.
40+
import XHRUpload from '@uppy/xhr-upload';
41+
42+
// Your own metadata on files
43+
type Meta = { myCustomMetadata: string };
44+
// The response from your server
45+
type Body = { someThingMyBackendReturns: string };
46+
47+
const uppy = new Uppy<Meta, Body>().use(XHRUpload, {
48+
endpoint: '/upload',
49+
});
50+
51+
const id = uppy.addFile({
52+
name: 'example.jpg',
53+
data: new Blob(),
54+
meta: { myCustomMetadata: 'foo' },
55+
});
56+
57+
// This is now typed
58+
const { myCustomMetadata } = uppy.getFile(id).meta;
59+
60+
await uppy.upload();
61+
62+
// This is strictly typed too
63+
const { someThingMyBackendReturns } = uppy.getFile(id).response;
64+
```
65+
66+
Happy inferring!
67+
68+
## Merging the two AWS S3 plugins
69+
70+
We used to two separate plugins for uploading to S3 (and S3-compatible
71+
services): `@uppy/aws-s3` and `@uppy/aws-s3-multpart`. They have different use
72+
cases. The advantages of multipart uploads are:
73+
74+
- Improved throughput – You can upload parts in parallel to improve throughput.
75+
- Quick recovery from any network issues – Smaller part size minimizes the
76+
impact of restarting a failed upload due to a network error.
77+
- Pause and resume object uploads – You can upload object parts over time. After
78+
you initiate a multipart upload, there is no expiry; you must explicitly
79+
complete or stop the multipart upload.
80+
- Begin an upload before you know the final object size – You can upload an
81+
object as you are creating it.
82+
83+
However, the downside is request overhead, as it needs to do creation, signing,
84+
and completion requests besides the upload requests. For example, if you are
85+
uploading files that are only a couple kilobytes with a 100ms roundtrip latency,
86+
you are spending 400ms on overhead and only a few milliseconds on uploading.
87+
This really adds up if you upload a lot of small files.
88+
89+
AWS, and generally the internet from what we found, tend to agree that **you
90+
don't want to use multipart uploads for files under 100MB**. But this sometimes
91+
puts users of our libraries in an awkward position, as their end users may not
92+
only upload very large files, or only small files. In this case a portion of
93+
their users get a subpar experience.
94+
95+
---
96+
97+
<!-- TODO: /aws-s3-multipart link should be /aws-s3 (needs site changes) -->
98+
99+
We’ve merged the two plugins into `@uppy/aws-s3` with a new
100+
[`shouldUseMultipart`](/docs/aws-s3-multipart/#shouldusemultipartfile) option!
101+
By default it switches to multipart uploads if the file is larger than 100MB.
102+
You can pass a `boolean` or a function to determine it per file.
103+
104+
## React hooks
105+
106+
People working with React are more likely to create their own user interface on
107+
top of Uppy than those working with "vanilla" setups. Working with our pre-build
108+
UI components is a plug-and-play experience, but building on top of Uppy’s state
109+
with React primitives has been tedious.
110+
111+
To address this we’re introducing to new hooks: `useUppyState` and
112+
`useUppyEvent`. Thanks to the TypeScript rewrite, we can now do powerful
113+
inference in hooks as well.
114+
115+
### `useUppyState(uppy, selector)`
116+
117+
Use this hook when you need to access Uppy’s state reactively.
118+
119+
```js
120+
import Uppy from '@uppy/core';
121+
import { useUppyState } from '@uppy/react';
122+
123+
// IMPORTANT: passing an initializer function to prevent Uppy from being reinstantiated on every render.
124+
const [uppy] = useState(() => new Uppy());
125+
126+
const files = useUppyState(uppy, (state) => state.files);
127+
const totalProgress = useUppyState(uppy, (state) => state.totalProgress);
128+
// We can also get specific plugin state.
129+
// Note that the value on `plugins` depends on the `id` of the plugin.
130+
const metaFields = useUppyState(
131+
uppy,
132+
(state) => state.plugins?.Dashboard?.metaFields,
133+
);
134+
```
135+
136+
<!-- TODO: this permalink to State will get outdated. Maybe put it in a file we can link to? -->
137+
138+
You can see all the values you can access on the
139+
[`State`](https://github.com/transloadit/uppy/blob/dab8082a4e67c3e7f109eacfbd6c3185f117dc60/packages/%40uppy/core/src/Uppy.ts#L156)
140+
type. If you are accessing plugin state, you would have to look at the types of
141+
the plugin.
142+
143+
### `useUppyEvent(uppy, event, callback)`
144+
145+
Listen to Uppy [events](/docs/uppy/#events) in a React component.
146+
147+
Returns an array of which the first item is an array of results from the event.
148+
Depending on the event, that can be empty or have up to three values. The second
149+
item is a function to clear the results.
150+
151+
Values remain in state until the next event (if that ever comes). Depending on
152+
your use case, you may want to keep the values in state or clear the state after
153+
something else happened.
154+
155+
```ts
156+
import Uppy from '@uppy/core';
157+
import { useUppyEvent } from '@uppy/react';
158+
159+
// IMPORTANT: passing an initializer function
160+
// to prevent Uppy from being recreated on every render.
161+
const [uppy] = useState(() => new Uppy());
162+
163+
const [results, clearResults] = useUppyEvent(uppy, 'transloadit:result');
164+
const [stepName, result, assembly] = results; // strongly typed
165+
166+
useUppyEvent(uppy, 'cancel-all', doSomethingElse);
167+
```
168+
169+
## Google Photos
170+
171+
A long requested feature is finally here: Google Photos support!
172+
173+
:::info
174+
175+
Uppy can bring in files from the cloud with [Companion](/docs/companion/).
176+
177+
Companion is a hosted, standalone, or middleware server to take away the
178+
complexity of authentication and the cost of downloading files from remote
179+
sources, such as Instagram, Google Drive, and others.
180+
181+
This means a 5GB video isn’t eating into your users’ data plans and you don’t
182+
have to worry about OAuth.
183+
184+
:::
185+
186+
<!-- TODO: video of the Google Photos plugin -->
187+
188+
[`@uppy/google-photos`](/docs/google-photos/) is a new plugin so you can use it
189+
next to your existing [`@uppy/google-drive`](/docs/google-drive/) plugin.
190+
191+
## UX improvements for viewing remote files
192+
193+
When using [Dashboard](/docs/dashboard) with any of our remote sources (such as
194+
Google Drive) you use our internal `@uppy/provider-views` plugin to navigate and
195+
select files.
196+
197+
We now made a handful of quality of life improvements for users.
198+
199+
| Before | After |
200+
| :-----------------------------------: | :---: |
201+
| ![yeah](/img/blog/3.13-3.21/crop.mov) | yeah |
202+
203+
<!-- TODO: video of the improvements-->
204+
205+
- **Folder caching**. When naviging in and out of folders, you now no longer
206+
have to wait for the same API call — you’ll see the results instantly.
207+
- **Indeterminate checkmark states**. Before going into a folder we can’t know
208+
how many files it contains so checking it immediately will show a traditional
209+
checkmark. But when you go into a folder and you only select a subset of the
210+
files, we’ll now show the indeterminate checkmark for the folder when you
211+
navigate back out. Making it more clear you’re only uploading some of the
212+
files in that folder.
213+
- **Reworked restrictions**. Uppy supports file
214+
[restrictions](/docs/uppy/#restrictions), such as max number of files and max
215+
file size. It’s now immediately clear in the UI when you are exceeding the max
216+
number of files you can select. The error notifications are now also more
217+
clear.
218+
- **Shift-click multi-select fixes**. You can shift-click the checkboxes to
219+
select many files at once. This did not always work correctly and it also
220+
highlighted the files names, which we now improved.
221+
222+
We’re confident this turns our interface for remote sources into the most
223+
advanced one out there. We’ve seen some competing libraries not even aggregating
224+
results beyond the first page API limit of providers.
225+
226+
## Revamped options for `@uppy/xhr-upload`
227+
228+
## Simpler configuration for `@uppy/transloadit`
229+
230+
## Companion
231+
232+
## And more
233+
234+
The 4.0 release contained over 170 contributions, many too small to mention, but
235+
together resulting in Uppy continuing to grow and improve. We closely listen to
236+
the community and are always looking for ways to improve the experience.
237+
238+
Since our last blog post other (non-breaking) changes have also been released.

0 commit comments

Comments
 (0)