---
title: "Custom Upload"
slug: "custom-upload"
category: "Widgets"
reactComponent: "ArcWidgetUpload"
reactTier: "premium"
reactSince: "0.1.0-alpha.5"
reactExample: "upload.tsx"
locale: "es"
hosts: "ninox"
---

# Custom Upload

## AI Defaults (read first)

- **uniqueId**: Required, `"upload-" + Nr`.
- **image.tableId**: Required – target table for new records. Ninox-handler only — core emits semantic file payload.
- **image.fieldId**: The text field for Base64 data. Platform-dependent (Web vs. App may differ). Adapter-only.
- **Persistence**: Core emits `upload` with `{ name, src, width?, height?, original? }` where `original` is an optional Base64 string (PDF source when `convertSettings.originalFile` is set). Ninox handler builds `update` / `create` / `popup` from `ui.data.image.*` and resolves `originalFile.fieldId` / `recordId` from `convertSettings`. React: `onUpload(payload)`.
- **container**: `height: "500px"`, `width: "100%"`.
- **multiupload**: Default `false`, set `true` for multiple files at once.
- **capture**: Default `false`, set `true` to open camera directly.
- **clipboard**: Optional `clipboard: true` — pegar con Ctrl/Cmd+V o al hacer clic, cuando haya archivos en el portapapeles.
- **drag**: Default **on** for the empty dropzone (hover + drop). Custom `container.value` stays opt-in via `drag: true` or `{ enabled, label?, target?, targetId? }`. `drag: false` opts out.
- **image.format / image.quality**: Optional encode — `format: "jpeg"` | `"webp"` | `"original"`, `quality: 0–1` (default `0.8` when format is set). Offline en el navegador; útil para muchas fotos en informes PDF.
- **logs**: Optional `logs: true` — tamaños de codificación + vista previa de la imagen (consola + preview debajo del widget).
- **Two Base64 fields**: Some setups need separate fields for Web and App – check your schema.

# Custom Upload

<iframe src="https://www.youtube.com/embed/wGEOv0xiAKY?iv_load_policy=3&rel=0&modestbranding=1&playsinline=1&autoplay=1&mute=1" data-thumbnail="Medium Quality" frameborder="0" allow="presentation; fullscreen; accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"></iframe>Con `arcCustomUpload` puedes subir imágenes y archivos directamente en Ninox — de uno en uno o en selección múltiple. Al hacerlo se crea automáticamente un registro propio por cada archivo, incluyendo el contenido en Base64, el nombre del archivo e información adicional opcional.

El widget resuelve una de las mayores limitaciones de Ninox: el incómodo upload múltiple. En lugar de complicados workarounds, ahora se abre con un clic el explorador de archivos o la cámara — y esto **en cualquier interfaz**: ya sea en un dashboard, mediante un Custom Button o dentro de un layout propio.

Las imágenes subidas se guardan primero como texto Base64 en un campo de tu elección y desde ahí se pueden:

- mostrar directamente con el widget [Custom Image](/documentation/images),
- convertir mediante script en un campo de imagen de Ninox (recomendado),
- o simplemente guardar como archivo.

También en **dispositivos móviles** el widget es ideal: puedes fotografiar con la cámara o seleccionar y subir varias imágenes de la galería, a tu elección. Esto ahorra al usuario muchos clics y simplifica notablemente el mantenimiento de datos.

⚠️ **Nota:** En la **app de MacOS** así como en la **app de Android** actualmente la función de multi-upload no funciona. Aquí las restricciones vienen dadas por parte de Ninox.

## Formatos compatibles

- **Imágenes**: JPG, PNG, SVG
- **Documentos**: PDF
- **Tablas y datos**: CSV, XML, XLS, XLSX

**Nota:** Los archivos PNG y SVG se convierten actualmente de forma automática a JPG. Para un procesamiento posterior de alta calidad, recomendamos convertir los datos Base64 a archivos reales lo antes posible y guardarlos como campo de archivo en Ninox.

### HEIC/HEIF (fotos de iPhone)

Los iPhones modernos guardan las fotos por defecto en **formato HEIC**. Aquí aplica:

| Método de subida | ¿Funciona? | Explicación |
|----------------|---------------|-----------|
| **Directamente desde iPhone/iPad** | ✅ Sí | iOS convierte automáticamente a JPEG |
| **Escritorio → Safari (macOS)** | ✅ Sí | Safari admite HEIC de forma nativa |
| **Escritorio → Chrome/Firefox/Edge** | ❌ No | Estos navegadores no pueden decodificar HEIC |

💡 **Consejo para subidas desde escritorio:** Si los usuarios transfieren fotos del iPhone al escritorio con regularidad y quieren subirlas desde ahí, recomendamos el siguiente ajuste en el iPhone:

> **Ajustes → Cámara → Formatos → Máxima compatibilidad**

Así las fotos se guardan directamente como JPEG y son compatibles con todos los navegadores.

![](https://framerusercontent.com/images/tfmYk7LEFF3TRtIAdgEuiSIf1hs.png)

## Código de aplicación

Aquí un ejemplo sencillo de cómo puedes usar `arcCustomUpload` en una función — por ejemplo integrado en una [tabla](https://www.arc-rider.de/documentation/custom-table) (`embedded:true`) o de forma independiente en un formulario de Ninox en un campo de fórmula (`embedded:false`):


```javascript
let current := this;
arcCustomUpload({
        uniqueId: "my-Files-" + Nr,
        embedded: true,
        capture: false,
        multiupload: true,
        container: {
            icon: "",
            label: "Files Upload",
            height: "500px",
            width: "100%",
            value: ""
        },
        image: {
            tableId: "B",
            fieldId: if ninoxApp() = "web" then
                fieldId("Bilder", "base64_web")
            else
                fieldId("Bilder", "base64_app")
            end,
            width: 1500,
            height: 1500,
            widthFieldId: "J",
            heightFieldId: "K"
        },
        filename: {
            tableId: "B",
            fieldId: fieldId("Bilder", "Dateiname")
        },
        convertSettings: {
            type: ""
        },
        changeFieldValues: [{
                fieldId: "B",
                value: number(current.Nr)
            }, {
                fieldId: "E",
                value: 12
            }],
        setNewRecordId: [{
                recordId: current.Nr,
                fieldId: fieldId(current.Nr, "triggerField_saveForBrowser")
            }]
    })
```

## Parámetros

### uniqueId

**Tipo:** `text`
**Obligatorio:** sí

***uniqueId*** lo asignas de forma individual y debería ser único. El sentido es: si creas varios uploads con distintos settings en tu interfaz, evitas que se sobrescriban los estilos.

**📌 Atención: **Si dos widgets de upload en la misma página usan el mismo `uniqueId`, esto puede provocar errores (p. ej. los uploads se sobrescriben o no se cargan correctamente).


```javascript
uniqueId: "my-Image-" + Nr,
```

### embedded

**Tipo:** `boolean`
**Default:** `false`

Con el parámetro `embedded` determinas si el widget de upload debe mostrarse **integrado en un elemento existente** (p. ej. un `Custom Layout`, `Custom Button`, etc.) — o si aparece como elemento independiente en la interfaz.

- `true` → El widget se adapta al contenedor circundante (p. ej. en marcos de layout fijos).
- `false` → El widget se representa libremente en la interfaz de Ninox (p. ej. como elemento independiente).

💡 **Nota: **Si usas `embedded: true`, asegúrate de que el elemento circundante (p. ej. un bloque de layout) tenga una altura/anchura definida. El campo de upload adopta automáticamente estas medidas.

### capture

**Tipo:** `boolean`
**Default:** `false`

Con el parámetro `capture` controlas si el widget abre directamente la **cámara en dispositivos móviles** o si el usuario puede seleccionar un archivo manualmente.

- `true` → En smartphones compatibles, al hacer clic se abre directamente la app de cámara.
- `false` → Se abre como es habitual el explorador de archivos o la galería de fotos.

💡 **Nota: **Esta función solo funciona en dispositivos móviles (Apple) y en combinación con determinados navegadores. En escritorio el parámetro se ignora.


```javascript
capture: "", // Default: false
capture: false,
```

### multiupload

**Tipo:** `boolean`
**Default:** `false`

Con `multiupload` puedes definir si se pueden subir **varios archivos a la vez**.

- `true` → El usuario puede seleccionar varios archivos en el diálogo de archivos (p. ej. varias imágenes de la galería).
- `false` → Solo se puede subir un archivo por proceso de upload.

💡 **Consejo: **Especialmente práctico en formularios donde se deben subir varias fotos o documentos a la vez y guardarse como registros individuales.

### logs

**Tipo:** `boolean` o `{ enabled: true }`
**Default:** `false`

Con `logs: true` el widget escribe **estadísticas de codificación/redimensionado** en la consola del navegador (DevTools) y muestra debajo del upload una **vista previa** del resultado — útil para calibrar `image.format` e `image.quality`:

- Nombre de archivo y MIME
- Píxeles antes → después
- Tamaño antes → después y ahorro en %

```javascript
logs: true
```

Línea de ejemplo:

```text
[arcCustomUpload:…] IMG_1234.JPG image/jpeg → jpeg q=0.7 | 4032×3024→1200×900 | 3.2 MB → 180 KB (−94%)
```

### clipboard

**Tipo:** `boolean`
**Default:** `false`

Con `clipboard: true` se pueden pegar archivos desde el portapapeles — con **Ctrl+V / Cmd+V** o haciendo clic en el área de upload, cuando se han copiado archivos en el Finder/Explorador.

- Aparece un aviso tipo badge en cuanto el portapapeles contiene **archivos** (no solo texto).
- Si el portapapeles solo contiene texto, un clic abre el diálogo de archivos como es habitual.
- Varios widgets de upload en una página: solo reacciona el widget con el foco.

```javascript
clipboard: true
```

### drag

**Tipo:** `boolean` u `object`
**Default:** activo en la dropzone vacía estándar; desactivado en cuanto se establece `container.value`

La dropzone vacía (icono + etiqueta, sin `container.value`) acepta archivos mediante **arrastrar y soltar** y muestra feedback de hover y drop. Con un `container.value` propio, drag sigue siendo opt-in.

`drag: false` desactiva el drop en todas partes. `drag: true` o un objeto lo activa también para Custom Layouts:

```javascript
drag: true

// oder mit Optionen:
drag: {
    enabled: true,
    label: "Datei hier ablegen",
    target: "area",       // "area" | "first-child" | "layout"
    targetId: "meine-zeile-id"  // optional: externe Drop-Zone per DOM-ID
}
```

| `target` | Comportamiento |
|----------|-----------|
| `"area"` | Overlay sobre el área de upload |
| `"first-child"` | Estándar con `container.value` |
| `"layout"` | Primer `arcCustomLayout` dentro del widget |

Con `targetId` se puede usar como drop zone una **fila o área externa** (p. ej. una fila de tabla) — el ID se busca primero en la etiqueta de upload, y si no, en todo el documento.

### showLoadingPopup

**Tipo:** `boolean`
**Default:** `false`

Con `showLoadingPopup` puedes mostrar un **popup de carga durante el upload**. Esto es especialmente útil en:

- archivos de imagen grandes
- conexiones a internet lentas
- multi-uploads con muchos archivos

El popup bloquea la interfaz e informa al usuario de que el upload sigue en curso. Se cierra automáticamente en cuanto se han subido todos los archivos.

```javascript
showLoadingPopup: true,
```

💡 **Consejo:** Combina `showLoadingPopup: true` con `multiupload: true` para una mejor UX en uploads masivos.

En la **app de Ninox para iOS** el popup aparece de forma fiable en cuanto empieza el upload (el widget espera a que se muestre el modal antes de procesar los archivos).

### Bloque: container{}

**Tipo:** `object`
**Opcional**

El bloque `container` define el **aspecto y comportamiento del campo de upload**. Aquí puedes, por ejemplo, definir el tamaño o mostrar un texto propio en el área de upload.

Si no pasas ningún valor, se muestra un layout estándar con icono de upload y el texto «Seleccionar archivo».


```javascript
container: {
            icon: "",
            label: "Upload Image",
            height: "100%",
            value: "wert"
        },
```

**Parámetros posibles**

<figure><table><tbody><tr><th>Feld

</th><th>Typ

</th><th>Beschreibung

</th></tr><tr><td>`label`

</td><td>`text`

</td><td>Etiqueta (texto) en el campo de upload. Se muestra en el centro. Se muestra debajo del icono.

</td></tr><tr><td>`value`

</td><td>`widget` `text`

</td><td>Con value puedes diseñar tu propio diseño completamente personalizado del upload. Todo lo que indiques en value sobrescribe el diseño estándar existente. Aquí puedes usar, por ejemplo, un simple [Custom Button](/documentation/buttons) o diseñar un [Custom Layout propio](/documentation/custom-layout) que se adapte a tu UI. Este parámetro es opcional y por supuesto no es obligatorio rellenarlo.

</td></tr><tr><td>`height`

</td><td>`text`

</td><td>Altura del campo de upload, p. ej. `"80px"` o `"100%"`.

</td></tr><tr><td>`width`

</td><td>`text`

</td><td>Anchura del campo de upload, p. ej. `"100%"`, `"300px"` o `"auto"`.

</td></tr><tr><td>`icon`

</td><td>`widget`

</td><td>Con `[arcCustomIcon](https://www.arc-rider.de/documentation/custom-icon)` se puede definir un icono individual.

</td></tr></tbody></table></figure>💡 **Consejo: **Evita textos demasiado largos en `label`, ya que el widget trabaja centrado y de lo contrario la legibilidad se ve afectada. Lo ideal son 1–2 palabras o un símbolo con una explicación corta.

### Bloque: image{}

**Tipo:** `object`
**Campo obligatorio**

En el bloque `image` defines **dónde debe guardarse la imagen subida**. La imagen se escribe automáticamente en un campo de texto Base64 en la tabla indicada. Opcionalmente también puedes registrar por separado el ancho y el alto de la imagen.

<figure><table><tbody><tr><th>Feld

</th><th>Typ

</th><th>Beschreibung

</th></tr><tr><td>`tableId`

</td><td>`text`

</td><td>Tabla de destino en la que se guarda la imagen como registro.

</td></tr><tr><td>`fieldId`

</td><td>`text`

</td><td>ID del campo de texto en el que se escribe el contenido de la imagen en Base64.

</td></tr><tr><td>`widthFieldId`

</td><td>`text`

</td><td>*(opcional)* ID del campo para el ancho de imagen guardado (en píxeles). Crea para ello un campo numérico de Ninox.

</td></tr><tr><td>`heightFieldId`

</td><td>`text`

</td><td>*(opcional)* ID del campo para el alto de imagen guardado (en píxeles). Crea para ello un campo numérico de Ninox.

</td></tr><tr><td>`width` / `height`

</td><td>`text`

</td><td>*(opcional)* El width / height dentro del bloque image{} indica el ancho / alto en píxeles de tu imagen en el que quieres guardarla.

</td></tr><tr><td>`format`

</td><td>`text`

</td><td>*(opcional)* Formato de salida tras el upload (offline en el navegador). `"jpeg"` / `"jpg"`, `"webp"` u `"original"` (Default: omitir / `"original"` = comportamiento anterior, MIME de entrada). Con `"jpeg"`/`"webp"` la imagen siempre se vuelve a codificar — incluso si ya es más pequeña que `width`/`height`. El nombre de archivo recibe la extensión correspondiente (`.jpg` / `.webp`).

</td></tr><tr><td>`quality`

</td><td>`number`

</td><td>*(opcional)* Calidad de compresión de `0` a `1` (solo con `format: "jpeg"` o `"webp"`). Default `0.8` si `format` está establecido y falta `quality`. **No** aplica con `"original"`.

</td></tr></tbody></table></figure>

Los pasos de codificación se ejecutan **offline, directamente en el widget** (cámara y galería por igual). No se necesita ningún servicio en la nube. Nota: con `format: "jpeg"` se pierde la transparencia PNG (a propósito, para fotos/informes PDF).

```javascript
image: {
            tableId: "B",
            fieldId: if ninoxApp() = "web" then
                fieldId("Bilder", "base64_web")
            else
                fieldId("Bilder", "base64_app")
            end,
            width: 1500,
            height: 1500,
            widthFieldId: "J",
            heightFieldId: "K"
        },
```

**Ejemplo obra / informes PDF** (muchas fotos, mantener el PDF por email en unos 5–10 MB): la cámara (`capture: true`) y el upload múltiple desde galería (`multiupload: true`) usan la misma ruta de codificación. Para Ninox/`importFile` es preferible **JPEG** frente a WebP:

```javascript
image: {
            tableId: "B",
            fieldId: if ninoxApp() = "web" then
                fieldId("Bilder", "base64_web")
            else
                fieldId("Bilder", "base64_app")
            end,
            width: 1200,
            height: 1200,
            format: "jpeg",
            quality: 0.7,
            widthFieldId: "J",
            heightFieldId: "K"
        },
```

Con muchísimas fotos por informe (60+) prueba `width`/`height` a `1000` y/o `quality` a `0.6`. El tamaño final del PDF también depende del generador de informes — calíbralo con un informe real tras la configuración.

#### Nota importante de rendimiento

💡 **Consejo de UI/UX: **Los datos Base64 pueden llegar a ser muy grandes — especialmente con imágenes de alta resolución. Por eso recomendamos el siguiente flujo de trabajo:

- Configura un trigger de cambio en el campo Base64.
- Transfiere la imagen Base64 con `importFile()` a un campo de imagen real.
- Vacía después el campo Base64, para ahorrar espacio de almacenamiento.

Este trigger lo configuras así:


```javascript
Datei := importFile(Nr, base64, Dateiname);
shareURL := shareFile(Datei);
base64:=null;
```

Así los tiempos de carga y el tamaño de la base de datos se mantienen razonables — y aun así puedes seguir trabajando directamente con los datos de imagen.

🧠 **Importante: **Ninox trata los triggers de forma distinta en la app y en el navegador. Para que tu flujo de trabajo funcione de forma fiable **en todas las plataformas**, usa **dos campos base64 separados** (cada uno con opciones de almacenamiento distintas) — uno para Web, otro para la app:

- **App** -> Asignación por registro en el navegador
- **Web** -> Asignación por registro en el servidor

✅ Así te asegura que el upload se procesa automáticamente en **ambos entornos**.

### filename

**Tipo:** `object`
**Opcional**

En el bloque `filename` puedes indicar **en qué campo se debe guardar el nombre del archivo**. Esto resulta útil si más adelante quieres filtrar por archivos o mostrarlos en listas.

<figure><table><tbody><tr><th>Feld

</th><th>Typ

</th><th>Beschreibung

</th></tr><tr><td>`tableId`

</td><td>`text`

</td><td>Tabla de destino en la que se guarda el registro

</td></tr><tr><td>`fieldId`

</td><td>`text`

</td><td>ID del campo en el que se escribe el nombre del archivo

</td></tr></tbody></table></figure>💡 **Consejo: **Si más adelante sigues trabajando con `importFile()` o quieres volver a recuperar el archivo (p. ej. en una galería o lista de descargas), el nombre del archivo suele ser necesario — por eso es mejor guardarlo siempre.

### changeFieldValues

**Tipo:** `array of objects`
**Opcional**

Con `changeFieldValues` puedes **rellenar automáticamente campos adicionales en el registro de upload recién creado** — p. ej. con una referencia a la tabla actual, un tipo fijo o un valor de estado. Cada entrada contiene:

<figure><table><tbody><tr><th>Feld

</th><th>Typ

</th><th>Beschreibung

</th></tr><tr><td>`fieldId`

</td><td>`text`

</td><td>El campo del nuevo registro que se debe establecer

</td></tr><tr><td>`value`

</td><td>`any`

</td><td>El valor que se escribe en el campo

</td></tr></tbody></table></figure>
```javascript
changeFieldValues: [{
                fieldId: "B",
                value: number(current.Nr)
            }, {
                fieldId: "E",
                value: 10
            }]
```

### setNewRecordId

**Tipo:** `array of objects`
**Opcional**

Con `setNewRecordId` puedes, tras el upload, **enlazar el registro recién creado (p. ej. la imagen)** directamente en un registro existente — por ejemplo en un campo de referencia del formulario actual.

Esto resulta especialmente útil cuando necesitas:

- una **vinculación 1:1** con una imagen o documento,
- el elemento de upload **no está integrado en la tabla de destino**,
- o quieres establecer uploads de forma específica fuera de `changeFieldValues`.

<figure><table><tbody><tr><th>Feld

</th><th>Typ

</th><th>Beschreibung

</th></tr><tr><td>`recordId`

</td><td>`text`

</td><td>El valor del ID del registro existente en el que se debe enlazar

</td></tr><tr><td>`fieldId`

</td><td>`text`

</td><td>El campo de referencia que se rellenará con el nuevo registro de upload

</td></tr></tbody></table></figure>## Servicio adicional de conversión

La conversión de PDF mediante `convertSettings` **no forma parte del upload estándar**, sino que es un **servicio externo** proporcionado por arcRider.

Dado que cada conversión genera **costes de servidor**, su uso **tiene coste**. La facturación se realiza **por proceso de conversión**.

💬 Para el uso de este servicio creamos planes de precios individuales — según el volumen esperado y los requisitos concretos. Ponte en contacto con nosotros si quieres activar el servicio de conversión de PDF o tienes preguntas sobre las condiciones. ([office@arc-rider.com](mailto:office@arc-rider.com))

### convertSettings

**Tipo:** `object`
**Opcional – solo relevante en archivos PDF**

Si subes archivos PDF, con `convertSettings` puedes definir que se conviertan **automáticamente en archivos JPG / PNG / SVG** — p. ej. para planos técnicos o PDFs de formularios que deban poder mostrarse en Ninox.

<figure><table><tbody><tr><th>Feld

</th><th>Typ

</th><th>Beschreibung

</th></tr><tr><td>`fileType`

</td><td>`text`

</td><td>Formato de destino de la conversión. Actualmente compatible: `"jpg"`

</td></tr><tr><td>`apiKey`

</td><td>`text`

</td><td>Tu clave API personal para el servicio de conversión de PDF (solicítala a arcRider)

</td></tr><tr><td>`width`

</td><td>`number`

</td><td>Ancho de destino en píxeles (si no se establece `dpi`)

</td></tr><tr><td>`height`

</td><td>`number`

</td><td>Alto de destino en píxeles (si no se establece `dpi`)

</td></tr><tr><td>`dpi`

</td><td>`number`

</td><td>Resolución de la conversión en **dpi (puntos por pulgada)**

</td></tr><tr><td>`originalFile`

</td><td>`object`

</td><td>Opcional: campo para guardar el archivo PDF original como Base64

</td></tr></tbody></table></figure>## ✅ Conclusión

Con `arcCustomUpload` diseñas procesos de upload en Ninox de forma intuitiva, eficiente y flexible — ya sea en escritorio o en móvil. Mediante una parametrización específica controlas la presentación, las tablas de destino, las automatizaciones e incluso la conversión de PDFs.

Si quieres integrar uploads complejos en tu sistema o necesitas ayuda con la configuración, contáctanos sin compromiso — te ayudamos con la configuración óptima.
