<!-- https://svgrid.com/docs/help/web-components/sv-base/ - SvGrid documentation as markdown. Index of every page: https://svgrid.com/llms.txt -->

# `<sv-base>`: SvGrid Base as a custom element

[SvGrid Base](https://svgrid.com/docs/help/base/start/), tables with typed fields, linked records,
views, forms and automations, as one custom element for a page or a host with
no Svelte in its build: React, Vue, Angular, or a plain `<script>` tag. It is
part of `@svgrid/enterprise` (Suite edition): without a key it runs and shows
the pack's watermark and notice, as every entry does.

The element wraps `<SvBase>` and calls `enableBase()` itself.

## Loading it

```html
<script type="module" src="https://cdn.jsdelivr.net/npm/@svgrid/enterprise/dist/wc/sv-base-element.js"></script>

<sv-base id="base" height="480" editable sortable show-row-numbers column-adder field-menu></sv-base>
<script type="module">
  const base = document.getElementById('base')
  base.users = [{ id: 'u1', name: 'Ana Ruiz' }, { id: 'u2', name: 'Ben Okafor' }]
  base.currentUser = base.users[0]
  base.tables = [
    {
      id: 'tasks', name: 'Tasks', primaryField: 'title',
      columns: [
        { field: 'title', header: 'Task', fieldType: 'text' },
        { field: 'owner', header: 'Owner', fieldType: 'user' },
        { field: 'due', header: 'Due', fieldType: 'date' },
      ],
      data: [{ id: 't1', title: 'Draft the launch post', owner: 'u1', due: '2026-11-04' }],
    },
  ]
  base.addEventListener('tableschange', (e) => save(e.detail))   // the whole list, after every change
</script>
```

From npm, `import '@svgrid/enterprise/wc/base'` registers the element. The
tables take the same shape as `<SvBase tables>`: see
[the field types](https://svgrid.com/docs/help/fields/) and [linked records](https://svgrid.com/docs/help/base/linked-records/).

Registering is guarded: a page that loads the file twice (the CDN copy beside
an npm build, or one file under two URLs) keeps the first definition instead
of throwing on the second.

## The license key

```html
<sv-base license-key="YOUR-LICENSE-KEY"></sv-base>
```

The file carries its own copy of the pack, so `setLicenseKey()` imported from
`@svgrid/enterprise/license` sets the key of a different copy and the element
keeps its watermark. Pass the key to the element instead. The attribute is read
before the first render, so the watermark never shows; a key assigned later as
a property (`base.licenseKey = key`) takes the watermark and the notice down.

## Attributes vs properties

The [same rule as the grid element](https://svgrid.com/docs/help/web-components/sv-grid/#attributes-vs-properties):
primitives are attributes, arrays and objects are properties.

| Attribute | Property | Type |
| --- | --- | --- |
| `height` | `height` | pixels (`480`) or a CSS length (`100%`). Default 560. |
| `container-height` | `containerHeight` | the same as `height`, under `<SvBase>`'s name |
| `active-table` | `activeTable` | the id of the table shown first |
| `active-view` | `activeView` | the id of the view shown first (one table) |
| `primary-field` | `primaryField` | the field that names a record (one table) |
| `editable` | `editable` | `boolean` |
| `sortable` | `sortable` | `boolean` |
| `show-row-numbers` | `showRowNumbers` | `boolean` |
| `column-adder` | `columnAdder` | `boolean`: the "+" that adds a field |
| `field-menu` | `fieldMenu` | `boolean`: the header menu that edits, hides and deletes a field |
| `expand-rows` | `expandRows` | `boolean`: the button on each row that opens the record |
| `row-height` | `rowHeight` | `number` |
| `license-key` | `licenseKey` | `string` |

| Property only | Type |
| --- | --- |
| `tables` | `BaseTable[]`: several tables, shown as tabs |
| `data`, `columns`, `views` | one table, without tabs |
| `viewTypes` | the view types the view bar offers |
| `users` | the people user fields choose from (`setFieldUsers`) |
| `currentUser` | who is signed in: what "created by" and "last modified by" write (`setCurrentUser`) |
| `options` | any other `<SvBase>` or `<SvGrid>` prop, passed through as is |

A boolean attribute is on when present, whatever its value: `editable="false"`
is editable. Remove the attribute, or set the property to `false`.

`users` and `currentUser` are one list for the page, as they are in a Svelte
app: two elements that set different lists share the last one set.

## Events

| Event | `detail` |
| --- | --- |
| `tableschange` | the whole `tables` list, after an edit, a record added or deleted, a field, a view, an automation or a table changed |
| `viewschange` | the views of the one table (without `tables`; with it, a view change is a `tableschange`) |
| `remotechange` | changes made in the database by someone else, for tables with a realtime `source` |
| `ready` | `{ api }`, when the shown table's grid mounts. It fires again when another tab is opened. |

`ready` parks the api on the element (`base.api`) and `tableschange` parks the
list (`base.tables`), so code that runs later reads them without having caught
the event.

**Assigning `tables` loads it.** The element is upgraded the moment its file
loads, which on a plain page is before the script that sets its properties, so
the first `base.tables = [...]` arrives after an empty mount and is shown in its
place. A later assignment of a new list replaces what is on screen, edits
included, which is what reloading from a server wants. Handing back the list
`tableschange` gave you, or `base.tables` itself, is not a reload.

With `data` and `columns` instead of `tables`, the grid writes edits into the
row objects of `data` and no `tableschange` fires; use `tables` with one table
when the app needs to hear about every edit.

## React and Vue

Both wrappers register the element on import and hand every prop over as a DOM
property, so `tables` arrives as the array it is, also in React 18, which
would otherwise write it to an attribute as a string. Events become `onX`
callbacks in React and `@x` listeners in Vue.

```tsx
import { useRef } from 'react'
import { SvBase, type SvBaseHandle } from '@svgrid/enterprise/wc/base/react'

export function Projects({ tables, save }) {
  const base = useRef<SvBaseHandle>(null)
  return (
    <SvBase ref={base} tables={tables} editable height={560}
      onTableschange={(next) => save(next)} />
  )
}
```

```vue
<script setup>
import { SvBase } from '@svgrid/enterprise/wc/base/vue'
defineProps(['tables'])
const emit = defineEmits(['save'])
</script>

<template>
  <SvBase :tables="tables" editable :height="560" @tableschange="(next) => emit('save', next)" />
</template>
```

The ref's `element` is the `<sv-base>` itself, and `element.api` is the shown
grid's api.

## Styling

`<sv-base>` renders in the light DOM, like `<sv-grid>` and `<sv-sheet>`, so the
`--sg-*` theme tokens set on the page or on the element reach it, and its dialogs, menus and toasts portal to `document.body`. The element
draws the toaster that automation notices and save errors show in; with several
`<sv-base>` elements on a page, only the first one draws it.

## What it does not carry

Svelte snippets cannot cross the custom-element boundary, so `<SvBase>` props
that take one (a custom cell, a custom header) are not reachable. Custom field
types registered with `registerFieldType` are registered in the element's own
copy of the pack, which the page cannot import, so only the built-in field
types are available.

## See also

- [Demo 714](https://svgrid.com/demos/714-base-web-component/), the element
  driven from plain DOM code with an event log.
- [SvGrid Base](https://svgrid.com/docs/help/base/start/), the component behind the element.
- [`<sv-sheet>`](https://svgrid.com/docs/help/web-components/sv-sheet/), the spreadsheet as an element.

---

SvGrid is the Svelte 5 data grid (`npm install @svgrid/grid`, MIT). This page: https://svgrid.com/docs/help/web-components/sv-base/ . All docs: https://svgrid.com/llms.txt
