«file» Component Type

📖 Table of Contents

Introduction

The file component type lets you acquire, display and round-trip binary data (drafts, PDFs, images, …) as part of a SmarkForm form.

  • Imports and Exports: a String (a self-describing data URL) by default, or an Object when "format":"json" is set.
  • Singleton Pattern: supports the Singleton Pattern so a whole <div> drop-zone can act as the field.
  • Data Conversion: binary payloads are always stored internally as base64; encoding only changes how the payload appears in JSON exports and bare string imports.

A file field never renders a native OS picker by default: the authored input is repurposed as a visible, editable name field, and clicking it (or pressing Shift+Space) opens a hidden native file input. That hidden picker is never populated programmatically — files can only arrive from a real user gesture (pick, drag & drop or paste), or from the import() action.

Note: inferType() does not special-case native <input type="file"> elements ("file" falls through to "input"). You must always declare the file type explicitly with data-smark='{"type":"file",…}'.

Declaring a File Field

Real Field

The most direct form leaves the field as a native <input>:

<input
    type="file"
    data-smark='{"type":"file","name":"cv","accept":"application/pdf"}'
    placeholder="Click to pick a PDF, or drop it here…"
>

The type attribute may be omitted entirely — declaring "type":"file" in data-smark is enough. The visible field becomes a normal text input that shows the currently selected file name (which stays editable and wins on export when non-empty). A full, playable example follows in the Raw Format (data URL) section below.

The Singleton Pattern

Wrapping the field in any other element turns the whole wrap area into the field; it adopts the Singleton Pattern and applies drop and paste handlers over the entire container:

🔗
🗒️ HTML
🎨 CSS
⚙️ JS
👁️ Preview
📝 Notes
<div id="myForm">
    <div class="drop-zone" data-smark='{"type":"file","name":"doc","format":"json","encoding":"hex"}'>
        <input type="file" data-smark placeholder="Click, paste or drop a file here…">
        <button data-smark='{"action":"download"}'>Download</button>
    </div>
</div>
#myForm .drop-zone {
    border: 2px dashed #999;
    border-radius: .5rem;
    padding: 3rem 2rem;
    text-align: center;
}
#myForm .drop-zone button {
    margin-top: 1rem;
    text-align: center;
}

const myForm = new SmarkForm(document.getElementById("myForm"), {
    "value": {
    "doc": {
        "name": "payload.bin",
        "type": "application/octet-stream",
        "size": 3,
        "lastModified": 1700000000000,
        "data": "00ff0a"
    }
}
});

👉 Singleton container: the whole dashed box is the field.

  • Click anywhere on it (or press Shift+Space) to open the OS picker.
  • Drop files from your file manager straight onto the box.
  • Paste an image/screenshot onto it.

👉 Structured export: because this example uses "format":"json","encoding":"hex", the export is a plain object whose data is a hexadecimal string:

   {
     "name": "payload.bin",
     "type": "application/octet-stream",
     "size": 3,
     "lastModified": 1700000000000,
     "data": "00ff0a"
   }

👉 Partial imports are fine: import just { "name": "x.bin", "type": "…", "data": "…" }size and lastModified are recomputed / auto-completed for you.

👉 Download: the Download button gets the stored bytes back as a real browser download — the singleton container delegates to its inner field.

Try it! Load the demo value, then import the same object with only { "name": "renamed.bin", "data": "00ff0a" }.

Every example in this section comes with many of the following tabs:

  • HTML: HTML source code of the example.
  • CSS: CSS applied (if any).
  • JS: JavaScript source code of the example.
  • Preview: Live, sandboxed rendering of the example — fully isolated from the page styles.
  • Notes: Additional notes and insights for better understanding. Don't miss it‼️

✨ In the Preview tab, a JSON playground editor is available with handy buttons:

  • ⬇️ Export to export the form data to the JSON playground editor.
  • ⬆️ Import to import data from the JSON playground editor into the form.
  • ♻️ Reset in forms prefilled with sample data, resets the form to its default values.
  • ❌ Clear to clear the whole form.

💡 The JSON playground editor is part of the SmarkForm form itself — it is just omitted from the code snippets to keep the examples focused on what matters.

🛠️ Between the tab labels and the content there is always an edit toolbar:

  • ✏️ Edit — activates edit mode: each source tab turns into a syntax-highlighted code editor (powered by Ace) pre-filled with the full, merged source. Changes are sandboxed — the original example is not affected.
  • 📋 Include playground editor — (only visible in edit mode) controls whether the JSON playground editor is included in the preview. When toggled, the HTML and JS editors update instantly so you can see exactly what code is needed to add or remove it.
  • ▶️ Run — (only visible in edit mode) re-renders the Preview from the current editor contents and switches to the Preview tab.

The Singleton Pattern requires the container to hold exactly one field; otherwise a NOT_A_SINGLETON error is raised at render time. Any data-smark options declared on the container (e.g. accept, format, encoding, smark_file_*) are inherited by the inner field.

Importing and Exporting Data

The component’s interior state always stores the payload as plain base64. encoding only selects how the payload is written on JSON exports and how bare string payloads are decoded on imports; format selects between the data-URL string and the structured object. A data: URL is always base64, regardless of encoding.

Raw Format (data URL)

The default "format":"raw" exports a single, self-describing string:

data:text/plain;name=hello.txt;size=5;lastModified=1700000000000;base64,aGVsbG8=

The header carries the MIME type, the (URL-encoded) file name, the size in bytes and the last-modified timestamp; size is always recomputed from the payload on export.

🔗
🗒️ HTML
🎨 CSS
⚙️ JS
👁️ Preview
📝 Notes
<div id="myForm">
    <label data-smark="label">Resume (PDF):</label>
    <input
        type="file"
        data-smark='{"type":"file","name":"cv","accept":"application/pdf"}'
        placeholder="Click to pick a PDF, or drop it here…"
    >
</div>
#myForm input {
    display: block;
    width: 100%;
    box-sizing: border-box;
    padding: .5em .75em;
    border: 1px dashed #aaa;
    border-radius: .5rem;
}

const myForm = new SmarkForm(document.getElementById("myForm"), {
    "value": {
    "cv": "data:application/pdf;name=resume.pdf;size=5;lastModified=0;base64,aGVsbG8="
}
});

👉 Raw export: pick a file (or drop it on the field) and press ⬇️ Export — you get a single data-URL string.

👉 Import anything: paste one of the following into the JSON editor and press ⬆️ Import:

  • a data-URL string (name/size/lastModified come from the header),
  • a JSON object (or a JSON string of one) — partial objects are fine,
  • a bare base64 payload string,
  • null (clears the field).

👉 accept filters the picker AND drop/paste: try dropping a .png on this field — it is silently ignored (only application/pdf is accepted).

Try it! Load the demo cv value, then replace the imported name by editing the visible text field and export again.

Every example in this section comes with many of the following tabs:

  • HTML: HTML source code of the example.
  • CSS: CSS applied (if any).
  • JS: JavaScript source code of the example.
  • Preview: Live, sandboxed rendering of the example — fully isolated from the page styles.
  • Notes: Additional notes and insights for better understanding. Don't miss it‼️

✨ In the Preview tab, a JSON playground editor is available with handy buttons:

  • ⬇️ Export to export the form data to the JSON playground editor.
  • ⬆️ Import to import data from the JSON playground editor into the form.
  • ♻️ Reset in forms prefilled with sample data, resets the form to its default values.
  • ❌ Clear to clear the whole form.

💡 The JSON playground editor is part of the SmarkForm form itself — it is just omitted from the code snippets to keep the examples focused on what matters.

🛠️ Between the tab labels and the content there is always an edit toolbar:

  • ✏️ Edit — activates edit mode: each source tab turns into a syntax-highlighted code editor (powered by Ace) pre-filled with the full, merged source. Changes are sandboxed — the original example is not affected.
  • 📋 Include playground editor — (only visible in edit mode) controls whether the JSON playground editor is included in the preview. When toggled, the HTML and JS editors update instantly so you can see exactly what code is needed to add or remove it.
  • ▶️ Run — (only visible in edit mode) re-renders the Preview from the current editor contents and switches to the Preview tab.

Structured Format (format: “json”)

Setting "format":"json" exports a structured object instead of the string:

{
    "name": "payload.bin",
    "type": "application/octet-stream",
    "size": 3,
    "lastModified": 1700000000000,
    "data": "00ff0a"
}

data is the byte payload encoded per encoding. Imports accept the full object, a partial object (data is required; everything else may be omitted), a bare payload string, or a JSON string describing any of the above — see the Singleton Pattern example for each case.

Encodings

encoding (default "base64") controls two things:

Encoding JSON export data Bare string import decoding
base64 Standard base64 Base64
base64url URL-safe base64url Base64 / base64url
hex Lowercase hex Hex (0x-free)

It never affects data: URLs, which are always base64.

Empty Fields

An empty file field exports null (never a data URL with an empty payload). Importing null, undefined, an empty string or an empty JSON payload clears the field back to its empty state.

Options

accept

Comma-separated MIME types and/or extension suffixes (.pdf, .jpg). They are applied to the hidden native picker and to every drop/paste, so files that do not match are silently ignored.

format and encoding

"format": "raw" (default, data-URL string) or "json" (structured object). "encoding": "base64" (default), "base64url" or "hex".

Disabling acquisition

Each acquisition path can be turned off independently. All three default to true; set any of them to false to disable that route (e.g. "smark_file_drop":false on a container you want to use for dragging other content):

  • smark_file_open — click / Shift+Space opens the OS picker;
  • smark_file_drop — drag & drop is accepted;
  • smark_file_paste — clipboard paste is accepted.

Files in Lists (of: “file”)

Declaring a list whose item type is "file" gives you a collection of files:

<ul data-smark='{"type":"list","name":"photos","of":"file","min_items":0,"max_items":5}'>
    <li data-smark='{"role":"empty_list"}'>(Drop files here…)</li>
    <li data-smark='{"type":"file"}'>
        <input type="file" data-smark placeholder="Click to pick a file…">
    </li>
</ul>

Notice the item template root itself carries "type":"file" — each item is a file field directly, so the list imports/exports a flat array of data URLs (or JSON objects) instead of per-item objects. The list is also what you must use when you want to prevent SmarkForm from treating the file as the whole drop-zone: drag-reordering (Sortable) never populates files.

🔗
🗒️ HTML
🎨 CSS
⚙️ JS
👁️ Preview
📝 Notes
<div id="myForm">
    <button data-smark='{"action":"addItem","context":"photos"}' title="Add files">➕ Add files</button>
    <ul data-smark='{"type":"list","name":"photos","of":"file","min_items":0,"max_items":5}'>
        <li data-smark='{"role":"empty_list"}'>(Drop files here…)</li>
        <li data-smark='{"type":"file"}'>
            <input type="file" data-smark placeholder="Click to pick a file…">
        </li>
    </ul>
</div>
#myForm ul {
    list-style: none;
    padding-left: 0;
}
#myForm li {
    margin: .25rem 0;
}
#myForm li input {
    box-sizing: border-box;
    width: 100%;
    padding: .5em .75em;
    border: 1px dashed #aaa;
    border-radius: .5rem;
}

const myForm = new SmarkForm(document.getElementById("myForm"), {
    "value": {
    "photos": [
        "data:text/plain;name=a.txt;size=2;lastModified=0;base64,YWI=",
        "data:text/plain;name=b.txt;size=2;lastModified=0;base64,YmI="
    ]
}
});

👉 Array export: the list exports one entry per file — a flat array of data URLs (keep "format":"json" to get objects).

👉 Multi-pick: click — because the item type is file-capable, a single picker lets you select several files at once. A cancelled dialog leaves the list untouched.

👉 Drop: drag files from your OS straight onto the list; each is appended automatically as its own item.

👉 Limit (5): when more files are dropped/imported than max_items allows, you are asked to confirm adding only the first fitting files.

Try it! Load the demo value (two files) and then drop a third file onto the list.

Every example in this section comes with many of the following tabs:

  • HTML: HTML source code of the example.
  • CSS: CSS applied (if any).
  • JS: JavaScript source code of the example.
  • Preview: Live, sandboxed rendering of the example — fully isolated from the page styles.
  • Notes: Additional notes and insights for better understanding. Don't miss it‼️

✨ In the Preview tab, a JSON playground editor is available with handy buttons:

  • ⬇️ Export to export the form data to the JSON playground editor.
  • ⬆️ Import to import data from the JSON playground editor into the form.
  • ♻️ Reset in forms prefilled with sample data, resets the form to its default values.
  • ❌ Clear to clear the whole form.

💡 The JSON playground editor is part of the SmarkForm form itself — it is just omitted from the code snippets to keep the examples focused on what matters.

🛠️ Between the tab labels and the content there is always an edit toolbar:

  • ✏️ Edit — activates edit mode: each source tab turns into a syntax-highlighted code editor (powered by Ace) pre-filled with the full, merged source. Changes are sandboxed — the original example is not affected.
  • 📋 Include playground editor — (only visible in edit mode) controls whether the JSON playground editor is included in the preview. When toggled, the HTML and JS editors update instantly so you can see exactly what code is needed to add or remove it.
  • ▶️ Run — (only visible in edit mode) re-renders the Preview from the current editor contents and switches to the Preview tab.

Adding Files in Bulk

When the item type is file-capable, the list’s addItem action opens one multi-file picker before creating any item, so a cancelled dialog leaves the list untouched. Each chosen file becomes a regular item. Pass multiple: false to addItem() to force a single-file picker.

Dropping Files onto a List

OS drops and pastes over the whole list are converted through the item type’s toObjects() helper and appended one by one. Set "fileDrop":false on the list to disable this and keep drag & drop for reordering only.

Limits and Confirmation

When more files arrive than the remaining max_items slots, you are asked to confirm before adding only the first fitting files. On a full list an LIST_MAX_ITEMS_REACHED error is emitted. The same confirm-guarded truncation applies to bulk import through list.import() (LIST_IMPORT_OVERFLOW).

Downloading a File

A file field’s stored bytes can be handed back to the user as a real browser download with the download action:

<button data-smark='{"action":"download","context":"cv"}'>Download</button>

On an empty field the action is a no-op that returns null. The name used for the downloaded file follows this precedence:

  1. An explicit filename trigger option (any extra trigger data-smark property is passed through as an action option);
  2. The current visible name of the field if it was manually edited;
  3. The original stored file name.

Because the download needs a transient user gesture to be reliable, it is normally wired to a trigger button; calling download() programmatically takes effect as long as a user gesture is still active.

🔗
🗒️ HTML
🎨 CSS
⚙️ JS
👁️ Preview
📝 Notes
<div id="myForm">
    <div class="dl-row">
        <input data-smark='{"type":"file","name":"report"}' placeholder="Drop a file here or click to browse…">
        <button class="dl-btn" data-smark='{"action":"download","context":"report"}'>Download</button>
    </div>
    <p>
        <button class="dl-renamed" data-smark='{"action":"download","context":"report","filename":"report-copy.pdf"}'>Download as report-copy.pdf</button>
    </p>
</div>
#myForm .dl-row {
    display: flex;
    gap: .75rem;
}
#myForm .dl-row input {
    flex: 1;
    padding: .5em .75em;
    border: 1px dashed #aaa;
    border-radius: .5rem;
}
#myForm .dl-row button {
    padding: .5em 1em;
    border-radius: .5rem;
}
#myForm .dl-renamed {
    margin-top: 1rem;
}

const myForm = new SmarkForm(document.getElementById("myForm"), {
    "value": {
    "report": "data:text/plain;name=report.txt;size=5;lastModified=0;base64,SGVsbG8="
}
});

👉 Download: click Download and your browser starts a real download of the stored bytes with the original name.

👉 Empty field: with no file set the action is a no-op — nothing happens.

👉 Rename after the fact: the Download as report-copy.pdf button passes filename as a trigger option, overriding the stored name. Edit the visible name field first and the edited name wins over the stored one too.

Try it! Load the demo value, then rename the field to annual.pdf and click Download — the file arrives with that name. Then try the report-copy button.

Every example in this section comes with many of the following tabs:

  • HTML: HTML source code of the example.
  • CSS: CSS applied (if any).
  • JS: JavaScript source code of the example.
  • Preview: Live, sandboxed rendering of the example — fully isolated from the page styles.
  • Notes: Additional notes and insights for better understanding. Don't miss it‼️

✨ In the Preview tab, a JSON playground editor is available with handy buttons:

  • ⬇️ Export to export the form data to the JSON playground editor.
  • ⬆️ Import to import data from the JSON playground editor into the form.
  • ♻️ Reset in forms prefilled with sample data, resets the form to its default values.
  • ❌ Clear to clear the whole form.

💡 The JSON playground editor is part of the SmarkForm form itself — it is just omitted from the code snippets to keep the examples focused on what matters.

🛠️ Between the tab labels and the content there is always an edit toolbar:

  • ✏️ Edit — activates edit mode: each source tab turns into a syntax-highlighted code editor (powered by Ace) pre-filled with the full, merged source. Changes are sandboxed — the original example is not affected.
  • 📋 Include playground editor — (only visible in edit mode) controls whether the JSON playground editor is included in the preview. When toggled, the HTML and JS editors update instantly so you can see exactly what code is needed to add or remove it.
  • ▶️ Run — (only visible in edit mode) re-renders the Preview from the current editor contents and switches to the Preview tab.

Limitations

  • The native OS picker cannot be opened programmatically; file acquisition always requires a real user gesture (click / Shift+Space, drop, or paste). Consequently a cancelled picker is indistinguishable from “no selection”.
  • A zero-byte payload is only representable through a data-URL string import (it exports as a size:0 object). The object form requires a non-empty data — an empty or missing data clears the field to the null empty state, so picking/dropping a genuinely empty (0-byte) file is indistinguishable from picking none.
  • The picker itself is always multiple-capable; the list decides how many files are consumed (multiple: false on addItem limits the choice to a single file).
  • accept filtering is best-effort on drop/paste: it matches MIME types and extension suffixes, and non-matching files are silently ignored.