# Deploy a service
> Form that plans a service deployment from a template: host, free port, listen address, dependencies and an optional proxy domain.
- Element: `<sv-service-form>`
- React: `import { SvServiceForm } from "@/components/lumesec/sv-service-form"`
- Collection: Service Map (https://elements.lumesec.ai/components/service-map)
- Registry item: https://elements.lumesec.ai/r/sv-service-form.json
- Page: https://elements.lumesec.ai/components/service-map/service-form



Live preview: https://elements.lumesec.ai/view/sv-service-form

```html
<sv-service-form></sv-service-form>
```

## Installation

```bash
npx shadcn@latest add @lumesec/sv-service-form
```

First time with the @lumesec registry? Register it once, or install by URL:

```bash
npx shadcn@latest registry add @lumesec=https://elements.lumesec.ai/r/{name}.json
npx shadcn@latest add https://elements.lumesec.ai/r/sv-service-form.json
```

## Usage

React:

```tsx
import { SvServiceForm } from "@/components/lumesec/sv-service-form";

export function Example() {
  return (
    <SvServiceForm />
  );
}
```

HTML:

```html
<script type="module" src="https://elements.lumesec.ai/cdn/sv-service-form.js"></script>

<sv-service-form></sv-service-form>
```

## Behaviour

Start from a template, place it on a host, pick a free port, choose what it consumes and, if needed, a domain on the reverse proxy.

## API reference

### Events

Events bubble and cross the shadow boundary unless the description says otherwise.

| Event    | React prop | Detail                                                                                                                                                          | Description                                                                                                                                                                                                                                        |
| -------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `change` | `onChange` | `{ action: 'add-service', service: string, host: string, port: number, consumes: Array<'pgb' \| 'redis' \| 'idp' \| 's3' \| 'infer'>, domain: string \| null }` | Fires when the plan passes validation. `service` is the name, `host` a host id, `consumes` a list of service ids (`pgb`, `redis`, `idp`, `s3`, `infer`); `domain` is null unless reverse-proxy exposure is on. The listen address is not included. |

## Keyboard

| Keys                   | Action                                                                                                  |
| ---------------------- | ------------------------------------------------------------------------------------------------------- |
| ArrowLeft / ArrowRight | In the template group: select the previous or next template and move focus to it, wrapping at the ends. |

## Accessibility

* Templates are a `role="radiogroup"` of `role="radio"` buttons with `aria-checked`.
* Consumes is a `role="group"` of `aria-pressed` toggle buttons. Reverse-proxy exposure is a labelled `role="switch"`.
* On a validation error the field gets `aria-invalid="true"` and focus, and the message is in an assertive live region. `aria-invalid` is removed once the field is valid: the port is re-checked as it is typed, the name and domain on the next submit. The success message has `role="status"`.
* Reduced motion: the pixel burst on success is skipped and CSS transitions are switched off.

## Theming

The element reads your shadcn theme tokens through its shadow root, so light and dark follow your theme. The accent comes from `--lumesec`. See [Theming](/docs/theming).

This component reads `--border`, `--card`, `--foreground`, `--lumesec-success` and `--muted-foreground`.

To restyle only LumeSec elements, set the matching `--ui-*` overrides: `--ui-border`, `--ui-fg`, `--ui-mono`, `--ui-muted`, `--ui-ok` and `--ui-surface`.

## Notes

* Templates: PostgreSQL, Redis, HTTP API, Web app, Node exporter, Object store, Reverse proxy and Custom service. Choosing one fills the name and the next free port on the selected host, using `INFRA`. Changing the host re-checks the port but does not change it.
* Validation: the port is a whole number from 1 to 65535 (text such as `80abc` is rejected) and free on the host (the error offers the next free one); the name is required; with exposure on, the domain must look like `reports.lumesec.local` and must not already have a `/` route.
* Not form-associated: the element wraps its own `<form>` and does not submit with a surrounding form.
* Container query: single column under 460 px.


