File Upload

PRO

Configure file selection, drag and drop, file lists, and upload status.

Live preview
Interact with the component to see results here.
View code
HTML
<sy-file-upload
  label="Attachments"
  accept=".pdf,.png,.txt"
  max-size="5242880"
  max-files="3"
  multiple
  button-label="Choose files"
  drag-label="Drop files here"
  id="sample"
></sy-file-upload>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">View list</sy-button>
  <sy-button id="action-1" size="small">Clear all</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? 'Done';
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.getFiles();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget1 = document.querySelector("#sample");
  document.querySelector('#action-1').addEventListener('click', async () => {
    const result = await actionTarget1.clear();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
</script>

Usage guide

#

Show file selection and upload progress. Connect actual server uploads in your app.

  1. Write the error message in the error slot.

    Inside the component, add <span slot="error">Error message</span>. You can include links and other guidance as well as text. For inputs supporting noNativeValidity, enable it to show this guidance instead of a browser popup. Switch always displays errors below the component.

  2. Report business rule failures with setCustomError().

    For example, if the server rejects a duplicate value, update the error slot and call setCustomError(). This method does not take a message argument. A custom error also blocks form submission.

  3. Clear the error after resolving it and check whether submission is allowed.

    Use clearCustomError() to clear a business rule error. Required-field and format validation remain active. await checkValidity() returns true or false; reportValidity() validates and displays errors. Read valid named values through FormData on submission.

  1. Define selection constraints

    accept sets file types, maxSize sets the size in bytes, and maxFiles sets the maximum count.

  2. Connect uploads

    Read selected files from selected or changed and send them to the server.

  3. Update progress

    Update progress with setProgress(), and manage the list with removeFile() or clear().

Examples

#

File type, size, and count limits

Select up to two PNG or PDF files, each up to 5MB. Files are not sent to a server.

View code
HTML
<sy-file-upload
  accept=".png,.pdf"
  max-size="5242880"
  max-files="2"
  multiple
  button-label="Choose files"
  helper-text="PNG, PDF · Up to 5MB per file"
></sy-file-upload>

Customize the drop zone

Place instructions in the default slot while keeping click and drag-and-drop behavior.

View code
HTML
<sy-file-upload multiple>
  <div style="padding:20px;text-align:center"><strong>Add project files</strong>
    <p>Click here or drop files.</p>
  </div>
</sy-file-upload>

Selected files and reset

After adding files, use getFiles to inspect the list and clear to remove them all.

View code
HTML
<sy-file-upload
  multiple
  button-label="Choose files"
  id="sample"
></sy-file-upload>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">Read file list</sy-button>
  <sy-button id="action-1" size="small">Clear all</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? 'Done';
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.getFiles();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget1 = document.querySelector("#sample");
  document.querySelector('#action-1').addEventListener('click', async () => {
    const result = await actionTarget1.clear();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
</script>

Events and payloads

Interact with the component and inspect the latest event name and detail. The code includes subscriptions to events emitted by this component and its children.

View code
HTML
<sy-file-upload
  label="Attachments"
  accept=".pdf,.png,.txt"
  max-size="5242880"
  max-files="3"
  multiple
  button-label="Choose files"
  drag-label="Drop files here"
  id="sample"
></sy-file-upload>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">View list</sy-button>
  <sy-button id="action-1" size="small">Clear all</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>
<output id="demo-events" aria-live="polite">Interact with the component to see
  its latest event and payload.</output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? 'Done';
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.getFiles();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget1 = document.querySelector("#sample");
  document.querySelector('#action-1').addEventListener('click', async () => {
    const result = await actionTarget1.clear();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  for (const name of ["selected", "rejected", "changed", "removed", "focused", "blured"]) {
    document.addEventListener(name, event => {
      document.querySelector('#demo-events').textContent =
        `${event.target.localName} · ${name}: ${describeResult(event.detail)}`;
    });
  }
</script>

File selection state and keyboard focus

Add files, then select the first file to highlight its row. Deselecting does not delete a file. This example does not upload files to a server.

View code
HTML
<sy-file-upload
  label="Attachments"
  accept=".pdf,.png,.txt"
  max-size="5242880"
  max-files="3"
  multiple
  button-label="Choose files"
  drag-label="Drop files here"
  id="sample"
></sy-file-upload>
<sy-button id="select-first">Select first file</sy-button>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">Deselect</sy-button>
  <sy-button id="action-1" size="small">Read files</sy-button>
  <sy-button id="action-2" size="small">Move focus</sy-button>
  <sy-button id="action-3" size="small">Blur</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? 'Done';
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "multiple": true
  });
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.setSelectedFile("");
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget1 = document.querySelector("#sample");
  document.querySelector('#action-1').addEventListener('click', async () => {
    const result = await actionTarget1.getFiles();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget2 = document.querySelector("#sample");
  document.querySelector('#action-2').addEventListener('click', async () => {
    const result = await actionTarget2.setFocus();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget3 = document.querySelector("#sample");
  document.querySelector('#action-3').addEventListener('click', async () => {
    const result = await actionTarget3.setBlur();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  await (root => {
    const upload = root.querySelector('sy-file-upload');
    root.querySelector('#select-first').addEventListener('click', async () => {
      const [file] = await upload.getFiles();
      if (file) await upload.setSelectedFile(file.id);
    });
  })(document);
</script>

Forms and validation

Submit an empty value to trigger required-field validation. Enter a value and submit to inspect the form data, then try enabling and clearing a custom error.

View code
HTML
<form style="display:grid;gap:16px">
  <sy-file-upload
    name="field"
    required
    button-label="Choose files"
    id="sample"
  ><span slot="error">Choose a file.</span></sy-file-upload>
  <div style="display:flex;flex-wrap:wrap;gap:8px">
    <sy-button type="submit" variant="primary">Submit</sy-button>
    <sy-button type="reset">Reset</sy-button>
  </div>
</form>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">Check validity</sy-button>
  <sy-button id="action-1" size="small">Show error</sy-button>
  <sy-button id="action-2" size="small">Clear error</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? 'Done';
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.checkValidity();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget1 = document.querySelector("#sample");
  document.querySelector('#action-1').addEventListener('click', async () => {
    const result = await actionTarget1.setCustomError();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget2 = document.querySelector("#sample");
  document.querySelector('#action-2').addEventListener('click', async () => {
    const result = await actionTarget2.clearCustomError();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  document.querySelector('form').addEventListener('submit', event => {
    event.preventDefault();
    document.querySelector('#demo-result').textContent = describeResult([...new FormData(event
      .currentTarget)]);
  });
</script>

Read validation results and error states

Validate before submitting or display errors on screen. Check an empty required field, then fill it to see how the return value changes. Querying the state distinguishes custom errors from required-field errors.

View code
HTML
<form style="display:grid;gap:16px">
  <sy-file-upload
    name="field"
    required
    button-label="Choose files"
    id="sample"
  ><span slot="error">Choose a file.</span></sy-file-upload>
  <div style="display:flex;flex-wrap:wrap;gap:8px">
    <sy-button type="submit" variant="primary">Submit</sy-button>
    <sy-button type="reset">Reset</sy-button>
  </div>
</form>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">Check validity</sy-button>
  <sy-button id="action-1" size="small">Show error</sy-button>
  <sy-button id="action-2" size="small">Clear error</sy-button>
  <sy-button id="action-3" size="small">Validate with errors</sy-button>
  <sy-button id="action-4" size="small">Query validation state</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? 'Done';
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.checkValidity();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget1 = document.querySelector("#sample");
  document.querySelector('#action-1').addEventListener('click', async () => {
    const result = await actionTarget1.setCustomError();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget2 = document.querySelector("#sample");
  document.querySelector('#action-2').addEventListener('click', async () => {
    const result = await actionTarget2.clearCustomError();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget3 = document.querySelector("#sample");
  document.querySelector('#action-3').addEventListener('click', async () => {
    const result = await actionTarget3.reportValidity();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget4 = document.querySelector("#sample");
  document.querySelector('#action-4').addEventListener('click', async () => {
    const result = await actionTarget4.getStatus();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  document.querySelector('form').addEventListener('submit', event => {
    event.preventDefault();
    document.querySelector('#demo-result').textContent = describeResult([...new FormData(event
      .currentTarget)]);
  });
</script>

Custom error messages

Display a custom error for server-side duplicate checks or business rules. Change the message below and submit to verify that it is blocked. After clearing the error, required-field constraints must still be satisfied.

View code
HTML
<label for="error-message">Error message to display</label><input
  id="error-message"
  value="The server did not accept this value. Please review it."
  type="text"
>
<form style="display:grid;gap:16px">
  <sy-file-upload
    no-native-validity
    name="field"
    required
    button-label="Choose files"
    id="sample"
  ><span slot="error">Choose a file.</span></sy-file-upload>
  <div style="display:flex;flex-wrap:wrap;gap:8px">
    <sy-button type="submit" variant="primary">Submit</sy-button>
    <sy-button type="reset">Reset</sy-button>
  </div>
</form>
<div class="demo-actions">
  <sy-button id="show-custom-error" variant="primary">Show error</sy-button>
  <sy-button id="clear-custom-error">Clear error</sy-button>
</div>
<output id="custom-error-result" aria-live="polite"></output>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? 'Done';
  await (async function customErrorSetup(root) {
    const field = root.querySelector('#sample');
    const message = root.querySelector('[slot="error"]');
    const input = root.querySelector('#error-message');
    const output = root.querySelector('#custom-error-result');
    const update = async () => {
      message.textContent = input.value.trim() || _msg("m_4eea8cef81c6");
      await field.setCustomError();
      output.textContent = _msg("m_e00bd46236d3", [message.textContent]);
    };
    input.addEventListener('input', update);
    root.querySelector('#show-custom-error').addEventListener('click', update);
    root.querySelector('#clear-custom-error').addEventListener('click', async () => {
      await field.clearCustomError();
      output.textContent = await field.checkValidity() ? _msg("m_c454251167b2") : _msg(
        "m_5b8cb48627e5");
    });
    root.querySelector('form').addEventListener('invalid', () => {
      output.textContent = _msg("m_c702c7778166");
    }, true);
    root.querySelector('form').addEventListener('reset', () => {
      output.textContent = _msg("m_b90c2b4dc30f");
    });
    await update();
  })(document);
  document.querySelector('form').addEventListener('submit', event => {
    event.preventDefault();
    document.querySelector('#demo-result').textContent = describeResult([...new FormData(event
      .currentTarget)]);
  });
</script>

API reference

#

Hover over a name or focus it with the keyboard to see the code. On touch screens, tap the name.

<sy-file-upload>

Properties

Attribute / PropertyTypeDefaultDescription
booleanfalseBlock user interaction and keyboard focus.
booleanfalseFail form validation when no value is provided.
string''Accepted file extensions or MIME types.
booleanfalseAllow multiple file selection.
number0Maximum size of one file in bytes. Set to 0 for no limit.
number0Maximum selectable file count. Set to 0 for no limit.
string'files'Field name used in FormData.
string'Browse files'File selection button text.
string'Drop files here or browse'Instructions in the drop zone.
string''Helper text, such as file restrictions.
booleanfalseHide the list of selected files.
string''Label describing the item.
string''Help text about the input state.
booleanfalseUse the app's error UI instead of the browser's built-in error display. checkValidity still performs validation.
'' | 'default' | 'error' | 'warning' | 'success'''Status styling for the file upload area.

Events

EventDetailDescription
CustomEvent<{ files: SyUploadFileItem[] }>Fires after the user adds files (drag-drop or browse). Carries the accepted file items.
CustomEvent<{ file: File; reason: string }>Fires for each file rejected by format, size or count limits.
CustomEvent<{ files: SyUploadFileItem[]; added: SyUploadFileItem[]; removed: SyUploadFileItem[]; isValid: boolean; }>Fires whenever the internal file list changes (selection, removal, status update). `added` / `removed` carry the delta since the previous emit so the consumer can reconcile its own state without diffing the full list.
CustomEvent<{ file: SyUploadFileItem; files: SyUploadFileItem[] }>Fires when a file is removed from the list.
CustomEvent<void>Emitted when focus is received.
CustomEvent<void>Emitted when focus is lost.

Methods

MethodReturnsDescription
Promise<void>Open the file chooser. Call within a user click handler.
Promise<void>Clear the selected file list.
Promise<void>Remove the file matching fileId from the list.
Promise<void>Update a file's upload progress and status.
Promise<SyUploadFileItem[]>Return the selected file list.
Promise<SyUploadFileItem | null>Return the file at the specified index.
Promise<void>Mark the specified fileId as the active file.
Promise<boolean>Return true if the current constraints are satisfied, otherwise false.
Promise<boolean>Validate the current value and display errors to the user.
Promise<void>Set a custom error state.
Promise<void>Clear the custom error state.
Promise<string>Return the current validation state.
Promise<void>Move keyboard focus to the input or action element.
Promise<void>Remove focus from the input element.

Slots

SlotDescription
Component body or child items.
Validation error message.
Input help message.