@uppy/components: fix dropzone global id (#5967)

fixes #5946 

Root Cause : 

`createDropzone` used a single global `id` for the file input
(`'uppy-dropzone-file-input'`) Clicking any `<Dropzone />` did
`document.getElementById(<global-id>).click()`, which always targeted
the first input in the DOM, so files were added to the first Uppy
instance.


9bac4c8398/packages/%40uppy/components/src/hooks/dropzone.ts (L73-L77)


**Solutions :**

Simplest solution would have been to just make the input id unique per
Uppy instance using `ctx.uppy.getID()`, and click that specific input.

```typescript

const fileInputId = 'uppy-dropzone-file-input-' + ctx.uppy.getID()

```
  **Caveats**:
  
If users don’t pass a custom id to `new Uppy()`, all instances default
to uppy, so ids still collide across instances.
  Multiple Dropzones under one instance still share the same id.
  

Switched to a ref-based approach so clicks trigger the input directly,
without relying on `document.getElementById` lookups. It still falls
back to a DOM click for backward compatibility.

**StackBlitz Link :** 


https://stackblitz.com/github/qxprakash/uppy/tree/debug_dropzone/examples/react?file=package.json&embed=1&view=editor&showSidebar=1&hideTerminal=1

  


**Update: Went for the ID based solution upon discussion with the team
as it's simpler.**
This commit is contained in:
Prakash 2025-09-17 14:09:57 +05:30 committed by GitHub
parent 48cfa09a44
commit d6b3aa575e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 12 additions and 7 deletions

View file

@ -0,0 +1,5 @@
---
"@uppy/components": patch
---
fix dom ID conflicts in dropzone and file-input

View file

@ -26,7 +26,7 @@ describe('App', () => {
const screen = render(<App />)
const fileInput = document.getElementById(
'uppy-dropzone-file-input',
'uppy-dropzone-file-input-uppy',
) as Element
await userEvent.upload(fileInput, createMockFile('test.txt', 'text/plain'))

View file

@ -26,7 +26,7 @@ describe('App', () => {
const screen = render(App)
const fileInput = document.getElementById(
'uppy-dropzone-file-input',
'uppy-dropzone-file-input-uppy',
) as Element
await userEvent.upload(fileInput, createMockFile('test.txt', 'text/plain'))

View file

@ -26,7 +26,7 @@ describe('App', () => {
const screen = render(App)
const fileInput = document.getElementById(
'uppy-dropzone-file-input',
'uppy-dropzone-file-input-uppy',
) as Element
await userEvent.upload(fileInput, createMockFile('test.txt', 'text/plain'))

View file

@ -25,8 +25,6 @@ export type DropzoneReturn<DragEventType, ChangeEventType> = {
}
}
const fileInputId = 'uppy-dropzone-file-input' as const
export function createDropzone<
DragEventType extends DragEvent,
ChangeEventType extends Event,
@ -34,6 +32,8 @@ export function createDropzone<
ctx: NonNullableUppyContext,
options: DropzoneOptions = {},
): DropzoneReturn<DragEventType, ChangeEventType> {
const fileInputId = `uppy-dropzone-file-input-${ctx.uppy.getID()}`
const handleDrop = (event: DragEventType) => {
event.preventDefault()
event.stopPropagation()

View file

@ -19,13 +19,13 @@ export type FileInputFunctions<EventType> = {
}
}
const fileInputId = 'uppy-file-input' as const
// Use a more generic constraint that works with both DOM Events and React/Vue Events
export function createFileInput<EventType extends Event>(
ctx: NonNullableUppyContext,
props: FileInputProps = {},
): FileInputFunctions<EventType> {
const fileInputId = `uppy-file-input-${ctx.uppy.getID()}`
const handleClick = () => {
const input = document.getElementById(fileInputId) as HTMLInputElement
input?.click()