> For the complete documentation index, see [llms.txt](https://docs.ninox.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ninox.com/builder-hub/de/visualize-and-organize-your-data/create-and-customize-pages/custom-component.md).

# Benutzerdefinierte Komponente

Erstelle benutzerdefinierte, datengesteuerte Seitenoberflächen mit Ninox Script und JSX-ähnlichem Markup.

Mit **Benutzerdefinierte Komponente** erstellst du eigene Oberflächen auf einer Seite. Schreibe dafür einen einzelnen Ninox-Script-Ausdruck, der ein Markup-Element zurückgibt.\
Du verwendest JSX‑ähnliche Tags direkt in Ninox Script. Kombiniere Live-Datensatzdaten, Buttons und Ereignisse mit fertig gestalteten Bausteinen. Dafür brauchst du keine separate Widget-Datei, keinen Bundler und keinen Upload.

Wenn du bereits ein Funktionsfeld erstellen kannst, kennst du bereits das Wesentliche. Ein Ausdruck in der **Benutzerdefinierten Komponente** wird im Kontext des aktuellen Datensatzes ausgewertet. Der einzige Unterschied besteht darin, dass er keine Zahl oder keinen Text zurückgibt, sondern eine Benutzeroberfläche.

## Benutzerdefinierte Komponente hinzufügen

{% stepper %}
{% step %}
**Tab Hinzufügen öffnen**

Öffne im **Einstellungsbereich** den Tab **Hinzufügen**.
{% endstep %}

{% step %}
**Benutzerdefinierte Komponente hinzufügen**

Ziehe die **Benutzerdefinierte Komponente** aus **Basiskomponenten** an die gewünschte Stelle auf der Seite.
{% endstep %}

{% step %}
**Benutzerdefinierte Komponente konfigurieren**

Erweitere in den **Einstellungen** der Komponente den Bereich **Allgemein**.\
Wähle unter **Logik** <i class="fa-code-simple">:code-simple:</i>, um den Skript-Editor zu öffnen.\
Gib den Ausdruck ein, der deine Benutzerdefinierte Komponente zurückgibt.
{% endstep %}

{% step %}
**Ergebnis prüfen**

Prüfe das erwartete Ergebnis. Ziehe die Komponente, um sie zu verschieben.\
Nutze die Anfasser, um ihre Größe anzupassen.
{% endstep %}
{% endstepper %}

{% hint style="info" %}
Bis du einen Ausdruck eingibst, erscheint der Platzhalter *"Gib einen Ninox-Skriptausdruck ein, der HTML zurückgibt."* Der Editor bietet KI-Unterstützung, die die hier beschriebene Syntax und ihre Einschränkungen bereits kennt.
{% endhint %}

## Schreibe deinen ersten Ausdruck

Dein Ausdruck muss den Typ **`react`** zurückgeben. Dabei handelt es sich um ein einzelnes Wurzelelement in Tag-Syntax. Alle übrigen Funktionen von Ninox Script funktionieren weiter: `let`, `function`, `if/else`, `for`, `switch`, `select` und die gesamte Funktionsbibliothek.

```ninox
<div class="p-4">
  <h2 class="text-lg font-bold">Hello, {first_name}</h2>
  <p class="text-default-500">Welcome back.</p>
</div>
```

Das Ergebnis wird direkt auf der Seite im Kontext des aktuellen Datensatzes gerendert. Gibt der Ausdruck nicht den Typ `react` zurück, siehst du: *„Der Ausdruck muss den Typ react zurückgeben. Verwende HTML-Tag-Syntax, um deine Oberfläche zu erstellen.“*

## Tags: HTML und Komponenten

Die Elementsyntax ist JSX‑ähnlich:

```ninox
<tag ...attributes>...children...</tag>     // with children
<tag ... />                                  // self-closing
```

Der **erste Buchstabe des Tag-Namens** bestimmt den Typ:

| Tag-Stil            | Bedeutung            | Beispiele                                                                                    |
| ------------------- | -------------------- | -------------------------------------------------------------------------------------------- |
| **Kleinschreibung** | Natives HTML-Element | `div`, `span`, `p`, `button`, `input`, `a`, `img`, `ul`, `li`, `h1`–`h6`, `table`            |
| **Großschreibung**  | Eine Komponente      | Deine eigene `function` oder eine integrierte Komponente wie `<Button>`, `<Card>`, `<Field>` |

Standard-HTML-Attribute funktionieren bei Tags in Kleinschreibung: `class`, `id`, `style`, `href`, `src`, `type`, `placeholder` usw.

Bei Tags in Großschreibung löst Ninox den Namen in dieser Reihenfolge auf: Zuerst verwendet Ninox deine eigene `function` mit diesem Namen. Gibt es keine, verwendet Ninox eine integrierte Komponente mit diesem Namen. Ist auch diese nicht vorhanden, erscheint die Warnung *„Funktion nicht gefunden“*.\
Benenne eigene Funktionen nicht nach integrierten Tags.

## Attribute: statisch und dynamisch

**Zeichenfolge in Anführungszeichen**: ein Literal

```ninox
<div class="card">
```

**Geschweifte Klammern**: ein beim Rendern ausgewerteter Ninox-Ausdruck

```ninox
<h2 title={first_name}>
<div class={if active then "bg-success" else "bg-default-200" end}>
```

**Attribut ohne Wert**: Kurzform für `={true}`

```ninox
<Button isIconOnly>          // same as isIconOnly={true}
```

Einfacher Text wird unverändert gerendert.

```ninox
<span>Hello</span>
```

`{ ... }` bettet den Wert eines Ausdrucks als Text ein.

```ninox
<span>{first_name}</span>
<div>Total: {format(amount, "#,##0.00")}</div>
```

Erstelle **Listen** mit einer Schleife, die ein Array von Elementen zurückgibt.

```ninox
<ul>
  {for item in items do
    <li>{item.name}</li>
  end}
</ul>
```

## Deine Datensatzdaten verwenden

Der Ausdruck läuft im Kontext des aktuellen Datensatzes, genau wie ein Funktionsfeld:

* Referenziere ein Feld über seinen Namen, etwa `first_name`, `amount` oder `status`.
* Greife auf verknüpfte Datensätze zu, etwa `Contacts.email` oder `line_items`.
* Verwende die gesamte Ninox-Script-Funktionsbibliothek, etwa `select`, `sum`, `format`, `openRecord`, `alert` und `icon`.

{% hint style="success" %}
Verwende `{ field_name }`, wenn du nur den Wert als Text benötigst. Verwende das Tag `<Field>`, wenn du das echte, bearbeitbare Widget benötigst.
{% endhint %}

## Event-Handler

Jedes Attribut, dessen Name mit `on` beginnt, etwa `onClick`, `onChange` oder `onPress`, ist ein Event-Handler. Er läuft beim Auslösen des Ereignisses, nicht beim Rendern. Er muss eine Funktion sein.

```ninox
// Correct — a function that runs on click
<button onClick={function () do alert("Hi") end}>Click me</button>

// Wrong — this runs immediately at render, not on click
<button onClick={alert("Hi")}>Click me</button>
```

Handler können Seiteneffekte auslösen, etwa eine Nachricht anzeigen, einen Datensatz öffnen, in ein Feld schreiben oder reaktiven Status aktualisieren.

```ninox
<Button onPress={function () do status := "done" end}>Mark done</Button>
```

Verwende einen Parameter, um Daten zum Ereignis zu lesen.

```ninox
<input onChange={function (e: any) do debug(e) end} />
```

Das empfangene Ereignis ist eine sichere Momentaufnahme. Es ist ein einfaches Objekt mit nur den relevanten primitiven Feldern, zum Beispiel:

* Ein Mausereignis stellt `clientX` / `button` bereit.
* Ein Tastaturereignis stellt `key` / `code` und ein verschachteltes `target` bereit (`id`, `name`, `value`, `checked`, `tagName` usw.).

Live-DOM-Knoten werden deinem Skript nicht direkt verfügbar gemacht.

{% hint style="success" %}
Bevorzuge bei HeroUI-Komponenten wie `<Button>` `onPress` statt `onClick`. Es verhält sich bei Maus, Touch und Tastatur einheitlich.
{% endhint %}

## Reaktiver Status mit `let`

Ein `let` in einem Ausdruck einer Benutzerdefinierten Komponente ist ein reaktiver Komponentenstatus. Er funktioniert wie ein kleiner Speicher:

* Seine Initialisierung läuft einmalig und setzt den Wert. Spätere Render-Vorgänge verwenden den gespeicherten Wert.
* Eine Neuzuweisung (`:=`) in einem Event-Handler speichert den neuen Wert und rendert die Komponente erneut.
* Nur Neuzuweisungen in einem Handler bleiben bestehen. Ein `let`, das du beim Rendern neu zuweist, etwa eine Schleifensumme, wird bei jedem Rendern zurückgesetzt. Es bleibt ein gewöhnlich berechneter Wert.
* Der Status gilt pro Instanz. Ein `let` im Schleifenrumpf oder in einer großgeschriebenen `function`-Komponente erhält jedes Mal einen eigenen unabhängigen Wert.
* Der Status wird zurückgesetzt, wenn sich der Datensatz ändert oder du den Ausdruck bearbeitest.

Zählerbeispiel:

```ninox
let count := 0;
<Button onPress={function () do count := count + 1 end}>
  { "Clicked " }
	{ count }
	{ " times" }
</Button>
```

Status pro Instanz in einer Komponente innerhalb einer Schleife:

```ninox
function Counter(label: text) do
  let n := 0;
  <div class="flex items-center gap-2">
    <span>{label}</span>
    <Button size="sm" onPress={function () do n := n + 1 end}>{n}</Button>
  </div>
end;
<div>{for t in tasks do <Counter label={t.title} /> end}</div>
```

Eine Berechnung zur Renderzeit (kein Status, wird bei jedem Rendern neu berechnet):

```ninox
let total := 0;
for i in line_items do total := total + i.amount end;
<div>Total: {total}</div>
```

{% hint style="info" %}
**Status im Vergleich zu Feldern**\
Die Neuzuweisung eines Feldes `status := "done"` schreibt in die Datenbank. Die Neuzuweisung eines `let` aktualisiert nur den UI-Status im Speicher.
{% endhint %}

## Das Tag `<Field>`

`<Field>` bindet ein Datenbankfeld direkt mit dem entsprechenden Ninox-Editor ein. Je nach Feldtyp wird beispielsweise ein Texteingabefeld, ein Datumswähler, eine Auswahlliste, ein Datei-Upload oder eine Referenzauswahl angezeigt. Es gibt nicht nur den Feldwert aus, sondern stellt ein voll funktionsfähiges Eingabeelement bereit. Das Tag ist selbstschließend.

```ninox
<Field field="first_name" />
```

### Attribute

<table><thead><tr><th width="113.92578125">Attribut</th><th width="106.45703125">Erforderlich</th><th>Beschreibung</th></tr></thead><tbody><tr><td><code>field</code></td><td>✅</td><td>Der Skriptname des Feldes.</td></tr><tr><td><code>module</code></td><td></td><td>Modulname. Standardmäßig der aktuelle Kontext.</td></tr><tr><td><code>table</code></td><td></td><td>Tabellenname. Standardmäßig der aktuelle Kontext.</td></tr><tr><td><code>row</code></td><td></td><td>Zeilen-ID (Zahl). Standardmäßig die Zeile des aktuellen Kontexts.</td></tr><tr><td><code>readonly</code></td><td></td><td>Rendert das Feld schreibgeschützt.</td></tr></tbody></table>

### So funktioniert die Zielauswahl

* Standardmäßig ist das Feld mit dem aktuellen Modul, der aktuellen Tabelle und dem aktuellen Datensatz verknüpft.

  ```ninox
  <Field field="first_name" />
  ```
* `module`, `table` und `row` greifen jeweils unabhängig auf den umgebenden Kontext zurück. Du musst daher nur die Werte angeben, die vom Standard abweichen.
* Sobald du `module` oder `table` überschreibst, gilt die aktuelle Zeile nicht mehr. Sie gehört zu einer anderen Tabelle. Gib daher eine explizite `row` an:

  ```ninox
  <Field module="crm" table="contacts" row={1} field="first_name" />
  ```
* `row` akzeptiert eine Zahl (`row={1}`), eine numerische Zeichenfolge (`row="1"`) oder einen Ausdruck (`row={this.id}`, `row={item.id}`).
* Mache ein Feld mit `readonly` schreibgeschützt:

  ```ninox
  <Field field="status" readonly />
  ```

Änderungen in einem `<Field>` werden in den Zieldatensatz zurückgeschrieben, außer bei `readonly`.

### Referenz- und Rückreferenzfelder

`<Field>` unterstützt Referenz- und Rückreferenzfelder vollständig. Die Auswahl wird wie an anderen Stellen in Ninox gerendert. Bei diesen Typen zeigt die eingebettete Auswahl die ersten fünf Spalten der referenzierten Tabelle.

{% hint style="success" %}
**Faustregel:** Verwende `<Field field="…" />` für das echte interaktive Widget. Verwende `{ field_name }`, wenn du nur den Wert als Text benötigst.
{% endhint %}

## Integrierte Komponenten (HeroUI)

Diese sofort einsatzbereiten Komponenten werden über Tags mit großgeschriebenen Namen eingebunden. Sie passen sich automatisch dem aktuell verwendeten Hell- oder Dunkelmodus an.

### **Verfügbare Komponenten**

| <ul><li><code>Button</code></li><li><code>Icon</code></li><li><code>Card</code></li><li><code>CardHeader</code></li><li><code>CardBody</code></li><li><code>CardFooter</code></li><li><code>Modal</code></li><li><code>ModalContent</code></li><li><code>ModalHeader</code></li></ul> | <ul><li><code>ModalBody</code></li><li><code>ModalFooter</code></li><li><code>Popover</code></li><li><code>PopoverTrigger</code></li><li><code>PopoverContent</code></li><li><code>Progress</code></li><li><code>Tabs</code></li><li><code>Tab</code></li></ul> | <ul><li><code>Table</code></li><li><code>TableHeader</code></li><li><code>TableColumn</code></li><li><code>TableBody</code></li><li><code>TableRow</code></li><li><code>TableCell</code></li><li><code>Tooltip</code></li><li><code>Field</code></li></ul> |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

### Button

Die Beschriftung steht in den Kindelementen.

* Häufige Props: `color`, `variant`, `size`, `radius`
* Boolean-Props: `isDisabled`, `isLoading`, `isIconOnly`, `fullWidth`
* Bevorzuge `onPress`.

```ninox
<Button color="danger" variant="flat" onPress={function () do alert("Deleted") end}>
  Delete
</Button>
```

### Card

```ninox
<Card shadow="sm" radius="lg">
  <CardHeader>Summary</CardHeader>
  <CardBody>{description}</CardBody>
  <CardFooter class="text-default-500">Updated today</CardFooter>
</Card>
```

`Card` unterstützt `shadow`, `radius`, `fullWidth`, `isHoverable` und `isPressable`.

### Tabs

Jedes `<Tab>` benötigt einen eindeutigen `key` und einen `title`.

```ninox
<Tabs>
  <Tab key="details" title="Details"><div>...</div></Tab>
  <Tab key="history" title="History"><div>...</div></Tab>
</Tabs>
```

### Table

Die Anzahl der Zellen muss den Spalten entsprechen. Gib jeder Zeile einen eindeutigen `key`.

```ninox
<Table isStriped>
  <TableHeader>
    <TableColumn>Name</TableColumn>
    <TableColumn>Amount</TableColumn>
  </TableHeader>
  <TableBody>
    {for i in line_items do
      <TableRow key={i.id}>
        <TableCell>{i.name}</TableCell>
        <TableCell>{format(i.amount, "#,##0.00")}</TableCell>
      </TableRow>
    end}
  </TableBody>
</Table>
```

`Table` unterstützt `isStriped`, `isCompact`, `hideHeader`, `removeWrapper` und `selectionMode`.

### Progress

```ninox
<Progress value={percent} maxValue={100} color="success" showValueLabel />
```

### Tooltip

`content` und genau ein Trigger-Kindelement. Verfügbare Props sind `placement`, `color`, `delay` und `closeDelay`.

```ninox
<Tooltip content="Delete this record" placement="top">
  <Button isIconOnly><Icon name="trash" /></Button>
</Tooltip>
```

### Popover

```ninox
<Popover placement="bottom" showArrow>
  <PopoverTrigger><Button>Options</Button></PopoverTrigger>
  <PopoverContent><div class="p-2">...</div></PopoverContent>
</Popover>
```

### Modal

Steuere die Sichtbarkeit mit einem Boolean-Wert für `isOpen`, der an ein reaktives `let` gebunden ist. Größen reichen von `sm` bis `5xl` oder `full`. Dieses Beispiel bettet auch bearbeitbare `<Field>`s ein:

```ninox
let open := false;
<div>
  <Button onPress={function () do open := true end}>Edit contact</Button>
  <Modal isOpen={open} onOpenChange={function () do open := false end}>
    <ModalContent>
      <ModalHeader>Edit contact</ModalHeader>
      <ModalBody class="flex flex-col gap-2">
        <Field field="first_name" />
        <Field field="email" />
      </ModalBody>
      <ModalFooter>
        <Button variant="light" onPress={function () do open := false end}>Close</Button>
      </ModalFooter>
    </ModalContent>
  </Modal>
</div>
```

`Modal` unterstützt:

* `isOpen`
* `onOpenChange`
* `onClose`
* `size`
* `placement`
* `backdrop`
* `radius`
* `scrollBehavior`
* Boolean-Props:
  * `isDismissable`
  * `isKeyboardDismissDisabled`
  * `hideCloseButton`
  * `shouldBlockScroll`.

## Eigene Komponenten mit `function`

Definiere wiederverwendbare Elemente mit einer großgeschriebenen `function`:

```ninox
function Badge(label: text) do
  <span class="badge">{label}</span>
end;
<Badge label="New" />
```

* Attribute werden anhand ihres Namens (nicht ihrer Position) Parametern zugeordnet. Für Parameter ohne passende Zuordnung wird `null` verwendet.
* Ein Parameter namens `children` (Typ `react[]`) erhält die verschachtelten Kindelemente:

  ```ninox
  function Card2(children: react[]) do
    <div class="card">{children}</div>
  end;
  <Card2><p>Inside the card</p></Card2>
  ```

Erstelle deine Benutzeroberfläche aus kleinen, wiederverwendbaren Komponenten.

## Styling mit Tailwind-Klassen

Gestalte Elemente mit Tailwind-Utility-Klassen im Attribut `class`. Beachte dabei eine wichtige Regel:

{% hint style="success" %}
**Es steht nur eine feste, geprüfte Auswahl an Tailwind-Utilities bereit.** Da Klassenzeichenfolgen zur Laufzeit berechnet werden, kann Tailwind sie beim Build nicht erkennen.\
Klassen **außerhalb der unterstützten Auswahl haben keine Wirkung**. **Klassen mit beliebigen Werten funktionieren nie.** Das betrifft alle Klassen mit eckigen Klammern wie `w-[473px]`, `bg-[#1e90ff]` oder `[&>div]:…`.
{% endhint %}

Verwende für individuelle Werte stattdessen das Attribut `style`:

```ninox
<div style={"width: " + text(px) + "px"}>
```

### Verfügbare Klassen

#### Layout

* `block inline-block inline flex inline-flex grid hidden`
* `flex-row/col/wrap`
* `items-*`
* `justify-*`
* `self-*`
* `grid-cols-1..6` (+`12`)
* `col-span-1..6/full`
* `flex-1 grow shrink`

#### Abstände

* `p/px/py/pt/pr/pb/pl`
* `m…` (einschließlich `m*-auto`)
* `gap/gap-x/gap-y`
* `space-x/space-y` mit den Werten `0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24`

#### Größen

* `w-full/auto/fit/screen` und Brüche wie `w-1/2`, `w-1/3`, `w-2/3`, `w-1/4` usw.
* `max-w-xs..4xl/full`
* `min-w-0`
* feste Höhen `h-4 … h-64`
* `h-full/auto/fit`
* `max-h-40/60/80/96/full`
* `size-4/6/8/10/12`

{% hint style="warning" %}
Es gibt keine Utilities für die Höhe des Wurzelelements wie `h-screen`. Versuche nicht, die Gesamthöhe der Komponente festzulegen.
{% endhint %}

#### Typografie

* `text-xs..4xl`
* `font-thin..extrabold`
* `text-left/center/right/justify`
* `italic`, `underline`, `line-through`, `uppercase`, `lowercase`, `capitalize`, `truncate`, `whitespace-nowrap`, `break-words`
* `leading-*`
* `tracking-*`

#### Farben

* `text-`/`bg-`/`border-` in allen Standard-Tailwind-Familien (`slate`…`rose`) mit Abstufungen von `50` bis `900` sowie `white`, `black`, `transparent`, `current`
* Bevorzuge die semantischen Theme-Tokens:\
  `default`, `primary`, `secondary`, `success`, `warning`, `danger`, `foreground`, `content1-4`, `divider`, `overlay` mit optionalen Suffixen wie `-50..900` oder `-foreground`. Beispiele sind `bg-primary`, `text-foreground`, `bg-content1`, `text-default-500` und `border-divider`. Diese passen sich automatisch an das helle oder dunkle Design an.

#### Rahmen und Eckenradius

* `border border-0/2/4/8`
* seitliche Rahmen\
  `border-solid/dashed/dotted`\
  `rounded … rounded-full`

#### Effekte

* `shadow shadow-sm..2xl shadow-inner`
* `ring ring-0/1/2/4`
* `opacity-0/25/50/75/90/100`.

#### Position und Überlauf

* `static`, `relative`, `absolute`, `fixed`, `sticky`
* `inset-0`
* `top/right/bottom/left-0`
* `z-0..50`
* `overflow-*`

#### Hintergründe, Anpassung und Seitenverhältnis

* `bg-cover/contain/center`
* `object-cover/contain/fill`
* `aspect-square/video`

#### Interaktivität und Bewegung

* `cursor-*`
* `select-*`
* `pointer-events-*`
* `transition`, `transition-colors`, `transition-transform`
* `duration-100/150/200/300/500`
* `ease-*`
* `scale-95/100/105`
* `rotate-45/90`

### Variantenpräfixe

Nur diese Präfixe werden erzeugt:

* `hover:`, `focus:`, `dark:` für Farb-Utilities
* `md:`, `lg:` für Layout-, Abstands-, Größen- und Text-Utilities

**Nicht verfügbar:** `active:`, `disabled:`, `group-hover:`, `sm:`, `xl:`, `2xl:`. Steuere Zustände wie aktiv oder deaktiviert mit einem Event-Handler und einem reaktiven `let`. Du kannst die Klassenzeichenfolge auch selbst berechnen:

```ninox
<div class={if selected then "bg-primary text-white" else "bg-content1" end}>
```

{% hint style="success" %}
Die integrierten HeroUI-Komponenten sind unabhängig von der Safelist immer vollständig gestaltet. Mit Komponenten wie `<Card>`, `<Button>` und ähnlichen erhältst du daher am einfachsten ein professionelles und konsistentes Erscheinungsbild.
{% endhint %}

## Die Option „Shadow DOM verwenden“

Jede Benutzerdefinierte Komponente verfügt über die Option **Shadow DOM verwenden** (standardmäßig deaktiviert).

Ist diese Option aktiviert, wird die Komponente innerhalb eines Shadow DOM gerendert, wodurch ihre Styles isoliert werden. Das CSS der Komponente kann keine Auswirkungen auf den Rest der Seite haben, und umgekehrt können Styles der Seite nicht in die Komponente hineinwirken. Aktiviere diese Option, wenn die Gestaltung einer Komponente vollständig unabhängig vom übrigen Seitenlayout sein soll.

Hinweise:

* Tailwind- und HeroUI-Klassen funktionieren weiterhin im Shadow DOM. Ninox lädt das Theme-Stylesheet in den Shadow Root.
* Da dieses Stylesheet asynchron geladen wird, kann der Inhalt beim ersten Rendern kurzzeitig ohne Styles angezeigt werden.
* Interaktivität wie Ereignisse, HeroUI-Popovers oder die Fokusverwaltung funktionieren auch über die Grenze des Shadow DOM hinweg. Es gibt keine funktionalen Unterschiede.

Lass die Option deaktiviert, wenn du keine Stilisolierung benötigst.

## Sicherheit und Einschränkungen

**Sicherheitsmechanismen**

* Dein Ausdruck erzeugt einen strukturierten Elementbaum, keinen HTML-String. Dadurch gibt es keine Angriffsfläche für die direkte Einbindung von HTML- oder JavaScript-Code.
* Bestimmte Tags werden blockiert: `script`, `iframe`, `object`, `embed`, `link`, `meta`, `base`, `body`, `head`, `html`, `title`. `<style>` ist erlaubt. Ist **Shadow DOM** aktiviert, werden die Styles auf die Komponente beschränkt.
* URLs in Attributen wie `href`, `src`, `action` und ähnlichen werden geprüft. Zulässig sind nur die Protokolle: `http`, `https`, `mailto` und `tel`. Das Protokoll `data:` ist ausschließlich für das Attribut `src` für Inline-Bilder erlaubt.
* Event-Handler verwenden nur Ninox Script und werden mit den regulären Berechtigungen deines Skripts ausgeführt. Die Ausführung von JavaScript-Code in Form von Zeichenketten wird nicht unterstützt.

Diese Sicherheitsmechanismen dienen der Inhaltsisolation für vertrauenswürdige Arbeitsbereich-Builder, also die Autoren der Ausdrücke. Wenn vollständig nicht vertrauenswürdiger Code von Drittanbietern ausgeführt werden soll, verwende stattdessen Custom Widgets. Diese werden in einer isolierten iframe-Sandbox ausgeführt.

**Wichtige Hinweise**

* Der Ausdruck muss einen Wert vom Typ `react` zurückgeben. Er benötigt ein einzelnes Wurzelelement, sonst wird nichts gerendert.
* Es stehen nur die freigegebenen Tailwind-Klassen zur Verfügung. Beliebige Werte und viele Varianten-Präfixe werden nicht unterstützt.
* Lege die Gesamthöhe der Komponente nicht im Wurzelelement fest. Die Seite verwaltet die Größe.
* Eingebettete Auswahlfelder für Referenzen und Rückreferenzen zeigen die ersten fünf Spalten der referenzierten Tabelle.
* Der reaktive `let`-Status wird zurückgesetzt, wenn sich der Datensatz ändert oder du den Ausdruck bearbeitest. Nur Neuzuweisungen in Handlern bleiben erhalten.
* Tritt in einer Komponente ein Fehler auf, bleibt dieser auf die betreffende Komponente beschränkt. Die übrigen Komponenten und die restliche Seite funktionieren weiterhin.

## Fehlerbehebung

<table data-search="false"><thead><tr><th>Symptom</th><th>Mögliche Ursache und Lösung</th></tr></thead><tbody><tr><td><em>„Der Ausdruck muss den Typ react zurückgeben.“</em></td><td>Dein Ausdruck endet nicht mit einem Tag. Der letzte Ausdruckswert muss ein Element sein.</td></tr><tr><td>Eine CSS-Klasse hat keine Wirkung</td><td>Die Klasse wird nicht unterstützt oder verwendet einen beliebigen Wert (<code>[...]</code>). Nutze eine unterstützte Klasse oder das Attribut <code>style</code>.</td></tr><tr><td>Ein Klick löst nichts aus</td><td>Der Handler muss eine Funktion sein: <code>onClick={function () do … end}</code>, nicht <code>onClick={doThing()}</code>.</td></tr><tr><td>Ein Zähler oder Schalter wird sofort zurückgesetzt</td><td>Du hast <code>let</code> beim Rendern neu zugewiesen statt innerhalb eines Handlers. Nur Neuzuweisungen in Handlern bleiben erhalten.</td></tr><tr><td><em>„Funktion nicht gefunden“</em> bei einem großgeschriebenen Tag</td><td>Es handelt sich weder um eine definierte <code>function</code> noch um eine integrierte Komponente. Prüfe Schreibweise und Groß-/Kleinschreibung.</td></tr><tr><td><code>&#x3C;Field></code>-Fehler: <em>„hat keine Zeile im Kontext“</em></td><td>Du hast <code>module</code> oder <code>table</code> angegeben und musst daher auch explizit <code>row</code> angeben.</td></tr><tr><td><code>&#x3C;Field></code>-Fehler: <em>„kann nur innerhalb einer Benutzerdefinierten Komponente verwendet werden“</em></td><td><code>&#x3C;Field></code> funktioniert nur innerhalb eines Ausdrucks einer Benutzerdefinierten Komponente.</td></tr><tr><td>Ein Tag fehlt in der Ausgabe</td><td>Der Tag kann blockiert sein, etwa <code>script</code> oder <code>iframe</code>. Möglich ist auch eine URL mit nicht erlaubtem Protokoll.</td></tr></tbody></table>

{% hint style="info" %}
Der AI-Assistent im Editor kennt die oben beschriebene Syntax und ihre Einschränkungen. Du kannst daher auch einfach beschreiben, was du erstellen möchtest, und den Ausdruck automatisch generieren lassen.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.ninox.com/builder-hub/de/visualize-and-organize-your-data/create-and-customize-pages/custom-component.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
