PHPackages                             tacoberu/nette-form-fileupload - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [File &amp; Storage](/categories/file-storage)
4. /
5. tacoberu/nette-form-fileupload

ActiveLibrary[File &amp; Storage](/categories/file-storage)

tacoberu/nette-form-fileupload
==============================

Uploading files has its specifics. It is not enough just to upload the file. We also want to show it when it's already uploaded (preferably with a preview). We want to possibly delete it or replace it with another version.

v2.0.2(2w ago)0453↓75%MITPHPPHP &gt;=8.1

Since Apr 25Pushed 2w ago1 watchersCompare

[ Source](https://github.com/tacoberu/nette-form-fileupload)[ Packagist](https://packagist.org/packages/tacoberu/nette-form-fileupload)[ RSS](/packages/tacoberu-nette-form-fileupload/feed)WikiDiscussions v2.0 Synced today

READMEChangelogDependencies (12)Versions (11)Used By (0)

Nette form FileControl
======================

[](#nette-form-filecontrol)

**FileControl** and **MultiFileControl** are Nette form inputs for file uploading that **behave exactly like other Nette inputs**: `setValue()` sets the value, `getValue()` returns it, and validation, conditional validation, and error messages work identically to text or select inputs.

The standard `` has several inconveniences when editing existing data:

1. The original file cannot be shown in any reasonable way — unlike other inputs, where the original value can be displayed.
2. Related to that is how to delete an existing file.
3. When an unrelated error occurs elsewhere in the form, the user has to upload the file again.
4. Uploading large files is a chapter of its own.

FileControl solves this:

1. An existing file is represented by a value of the `FileCurrent` class. If the user deletes it, the form knows about it.
2. Uploaded files are kept in a transaction — once uploaded, a file does not need to be uploaded again even if the form fails for another reason.

The input value can take one of three forms:

- `null` — no file, or the original file was deleted
- `FileUploaded` — a newly uploaded file (stored in the transaction, waiting to be committed to the system)
- `FileCurrent` — the original file stored in the system

Version and requirements
------------------------

[](#version-and-requirements)

BranchPHPNette`v2.0`&gt;= 8.1^3.2`v1.2`&gt;= 7.4^3.1Quick start
-----------

[](#quick-start)

Register the extension in `config.neon`:

```
extensions:
    filecontrol: Taco\Nette\Forms\Controls\FileControlExtension

filecontrol:
    store: Taco\Nette\Forms\Controls\UploadStoreTemp('uploading/txt-', null, %tempDir%)
```

Use in a form:

```
$form->addFileControl('portrait', 'Portrait');
$form->addMultiFileControl('attachments', 'Attachments');
```

A form with a single file (`FileControl`) — an uploaded file has a delete button:

[![Form with FileControl](docs/file.png)](docs/file.png)

A form with multiple files (`MultiFileControl`) — with image previews, deletion of individual items (✕) and the ↻ button for preloading:

[![Form with MultiFileControl](docs/files.png)](docs/files.png)

[![Typical form with an avatar](docs/bio.png)](docs/bio.png)

Runnable examples are available in the [`examples/`](examples/) directory.

Features
--------

[](#features)

### No-JS mode

[](#no-js-mode)

**The controls are fully functional without JavaScript.** The ↻ button lets the user upload files before submitting the form — the page does a full round-trip, but all form state is preserved. Validation, errors, and the transaction mechanism all work the same way.

### JS enhancements: AJAX upload and ↻ button helpers

[](#js-enhancements-ajax-upload-and--button-helpers)

The controls are fully functional without any JS (see above) — the ↻ button is visible and the user clicks it manually. On top of that baseline, `assets/filecontrol.ts` / `assets/filecontrol.js` offer optional JS enhancements that can be integrated into any frontend:

- `initMultiFileAjaxUpload(container)` — replaces the round-trip with an immediate AJAX upload for `MultiFileControl`. Activates based on the container's `data-upload-url` attribute — the library sets that itself whenever the control is rendered within a Presenter, so it's nothing you need to manage.
- `initFileAjaxUpload(container)` — the same for `FileControl`.
- `initMultiFileAutoPreload(container)` — for when no AJAX URL is available: hides the ↻ button and clicks it automatically once files are selected, so the round-trip happens on its own instead of requiring a manual click.
- `initFileHideOnNew(container)` — the equivalent for `FileControl`: hides the delete button and the original file's label once a new file is selected, so they don't get in the way.
- `initFileClearButton(fileInput)` — inserts a ✕ button after `` to clear the selected files.

AJAX upload features (`initMultiFileAjaxUpload` / `initFileAjaxUpload`):

- **Immediate upload on file selection** — no need to click ↻ or submit the form
- **Chunked transfer for large files** — files are automatically split so each POST stays within `upload_max_filesize`; the server reassembles them inside the transaction
- **Progress bar** — a `` element is shown during chunked transfers
- **Inline preview** — the server returns a thumbnail or filename label, inserted into the page without a full reload

```
import {
    initMultiFileAjaxUpload, initMultiFileAutoPreload,
    initFileAjaxUpload, initFileHideOnNew, initFileClearButton,
} from './filecontrol.js';

// data-upload-url is set by the library itself whenever the control is rendered
// within a Presenter — here it's only used to decide whether to wire up AJAX
// or the JS helper for the manual ↻ button.
document.querySelectorAll('[data-taco-type="file multiple"]').forEach(el => {
    el.dataset.uploadUrl ? initMultiFileAjaxUpload(el) : initMultiFileAutoPreload(el);
});
document.querySelectorAll('.taco-filecontrol-single').forEach(el => {
    el.dataset.uploadUrl ? initFileAjaxUpload(el) : initFileHideOnNew(el);
});
document.querySelectorAll('.taco-filecontrol-single input[type="file"]')
    .forEach(initFileClearButton);
```

### Validation

[](#validation)

Works exactly like other Nette inputs — fully compatible with `addConditionOn()`, `addRule()`, and error messages:

```
$form->addFileControl('portrait', 'Portrait')
    ->setRequired('Please select a file.')
    ->addRule($form::MaxFileSize, 'File is too large (max %d B).', 512 * 1024)
    ->addRule($form::MimeType, 'Only images are allowed.', ['image/jpeg', 'image/png'])
    ->addRule($form::Image, 'File must be an image.');
```

RuleDescription`Form::Required` / `setRequired()`A file must be selected or already exist as `FileCurrent`.`Form::MaxFileSize`Maximum file size in bytes.`Form::MimeType`Allowed MIME types, e.g. `'image/jpeg'` or an array of types.`Form::Image`Shorthand for supported image formats (`image/jpeg`, `image/png`, `image/gif`, `image/webp`).### Upload errors

[](#upload-errors)

- **A file exceeds `upload_max_filesize`** — PHP marks the file with `UPLOAD_ERR_INI_SIZE`; the control displays a message with the limit value.
- **The combined upload exceeds `post_max_size`** — PHP silently discards the entire POST body. The control detects this from `Content-Length` and adds a form-level error.

---

API
---

[](#api)

### setPreviewer()

[](#setpreviewer)

Sets a previewer for formatting file previews. `GenericFilePreviewer` displays image thumbnails.

### getRemoveButtonPrototype()

[](#getremovebuttonprototype)

Allows customizing the delete button: label, classes, title.

### Transactions

[](#transactions)

An uploaded file is automatically moved to the storage (transaction). This means the file does not need to be uploaded again on a validation error. After successful processing, it is available via `getValue()` like any other value.

Once committed to the system, the transaction can be discarded explicitly:

```
$form['portrait']->destroyStore();
```

Or it can be left to the GC, which deletes it automatically after `UploadStoreTemp::$gcAgeLimit` expires.

Building assets (TypeScript)
----------------------------

[](#building-assets-typescript)

```
npm run build:assets
```

Compiled output: `assets/filecontrol.js` (symlinked into `examples/document_root/js/`).

E2E tests (Playwright)
----------------------

[](#e2e-tests-playwright)

```
npm install
npx playwright install chromium  # first time only

npm run test:e2e        # run the tests
npm run test:e2e:ui     # interactive UI
```

The URL of the tested application is configured in `.env` via `APP_URL`. Outputs are saved to `temp/`.

###  Health Score

47

—

FairBetter than 93% of packages

Maintenance97

Actively maintained with recent releases

Popularity15

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity56

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~87 days

Recently: every ~0 days

Total

10

Last Release

14d ago

Major Versions

v1.1.x-dev → v2.0.02026-06-19

v1.2.0 → v2.0.x-dev2026-06-20

PHP version history (2 changes)v1.0.0PHP &gt;=8.1

v1.2.0PHP &gt;=7.4

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/1828339?v=4)[Martin Takáč](/maintainers/tacoberu)[@tacoberu](https://github.com/tacoberu)

---

Top Contributors

[![tacoberu](https://avatars.githubusercontent.com/u/1828339?v=4)](https://github.com/tacoberu "tacoberu (49 commits)")

---

Tags

nettefileuploadremoteformfileupload

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan, Rector

Type Coverage Yes

### Embed Badge

![Health badge](/badges/tacoberu-nette-form-fileupload/health.svg)

```
[![Health](https://phpackages.com/badges/tacoberu-nette-form-fileupload/health.svg)](https://phpackages.com/packages/tacoberu-nette-form-fileupload)
```

###  Alternatives

[kartik-v/yii2-widget-fileinput

An enhanced FileInput widget for Bootstrap 3.x, 4.x &amp; 5.x with file preview, multiple selection, and more features (sub repo split from yii2-widgets)

2357.1M97](/packages/kartik-v-yii2-widget-fileinput)[servocoder/richfilemanager

RichFilemanager - highly customizable open-source file manager

90964.5k2](/packages/servocoder-richfilemanager)[phery/phery

XAJAX alternative, phery.js is a library in PHP that maps to all jQuery functions, DOM manipulation, meta arguments and serialization, seamless ajax integration, RESTful emulation, form submission and partial rendering views, plus its PSR-0 compatible

13813.3k2](/packages/phery-phery)[presta/image-bundle

PrestaImageBundle is a Symfony bundle providing tools to resize uploaded and remote images before sending them through a classic form.

25159.5k](/packages/presta-image-bundle)[delight-im/file-upload

Simple and convenient file uploads — secure by default

6810.9k2](/packages/delight-im-file-upload)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
