Skip to main content

Web Component

@apiboost/omnispec ships a framework-agnostic Web Component (<omnispec-renderer>) alongside the React component. It is part of the free package — no additional install. Use it from vanilla HTML, Vue, Angular, Svelte, or any other framework that can render a DOM element.

Quick start

The fastest path is the standalone bundle — a single self-contained <script> that ships React, ReactDOM, and the renderer inline, with no build step on the consumer side.

<!doctype html>
<html>
<body>
<script src="https://unpkg.com/@apiboost/omnispec@latest/dist/wc/standalone.js"></script>
<omnispec-renderer
spec-url="https://petstore3.swagger.io/api/v3/openapi.json"
></omnispec-renderer>
</body>
</html>

For framework apps, prefer the subpath import so React/ReactDOM are shared with your existing tree:

import '@apiboost/omnispec/wc'
// <omnispec-renderer> is now registered globally.

Two configuration paths

Declarative HTML attributes

Best for scalar config like URLs, enums, and booleans:

<omnispec-renderer
spec-url="https://example.com/openapi.json"
theme-base="auto"
display-mode="compact"
navigation-mode="grouped"
allow-try-it="true"
proxy-url="/api/proxy"
></omnispec-renderer>

Imperative property assignment

Recommended for complex objects (parsed specs, theme overrides, sidebar nav):

import type { OmniSpecRendererElement } from '@apiboost/omnispec/wc'

const el = document.querySelector('omnispec-renderer') as OmniSpecRendererElement
el.spec = parsedSpecObject
el.theme = {
base: 'dark',
overrides: { '--omnispec-color-primary': '#8B5CF6' },
}
el.sidebarNav = {
items: [
{ id: 'home', label: 'Home', href: '/' },
],
}

You can also stringify-then-set the same complex props as JSON attributes — the element parses them with a try/catch and logs a console error on failure:

<omnispec-renderer
spec-url="/openapi.json"
theme='{"base":"dark"}'
></omnispec-renderer>

The imperative property always wins over an attribute when both are set.

Attribute reference

AttributeTypeDescription
spec-urlstringURL to fetch the spec from.
specJSON stringInline spec content (object or string) — see properties.
theme-base'light' | 'dark' | 'auto'Theme base. 'auto' follows the system preference.
theme-togglebooleanShow the floating theme toggle in 'auto' mode. Defaults to true.
themeJSON stringFull ThemeConfig (use the property in JS).
sidebar-navJSON stringSidebar nav config (use the property in JS).
display-mode'compact' | 'reference'Layout mode.
navigation-mode'grouped' | 'segmented'Nav grouping.
layout'sidebar' | 'stacked'Page layout.
sidebar-position'left' | 'right'Sidebar placement.
try-it-layout'inline' | 'panel'Try-It position in compact mode (default panel). panel = sticky right column; inline = collapsed below the operation. Ignored in reference mode.
schema-style'lines' | 'tokens' | 'chain' | 'table' | 'card'Schema tree style. table and card require Pro.
allow-try-itbooleanEnable Try-It.
interactive-oauthbooleanOpt out of the Pro interactive OAuth flow with "false" (forces manual token paste). No effect without Pro.
default-expand-operationsbooleanExpand every operation on load.
proxy-urlstringTry-It proxy endpoint.
server-urlstringOverride the API base URL for Try-It, ignoring the spec's servers.
try-it-persist-ttlnumber (ms)How long to persist Try-It auth and inputs in the browser.
download-linkboolean or URLShow a download button — pass true to use the spec URL or a string to specify a different URL.
docs-urlstringURL for the upgrade prompt's docs link.
upgrade-urlstringURL for the upgrade prompt's upgrade link.

Boolean attributes are truthy by default: any value other than 'false', '0', or 'off' (case-insensitive) — including a bare attribute with no value — is treated as true. To disable a boolean, set it to false/0/off or leave the attribute unset.

Property reference

For complex/JSON config, use the property API. Properties are available on the custom element after it has been created.

PropertyTypeDescription
specstring | objectURL, raw string, or parsed spec object.
themeThemeConfigFull theme config including overrides, onThemeChange.
sidebarNavSidebarNavConfigCustom sidebar navigation.

Events

The element dispatches standard CustomEvents that bubble and are composed through the shadow boundary. The detail property carries the typed payload.

Eventdetail typeWhen
spec-loadedSpecLoadedInfo ({ title, version, type })After the spec has been fetched and parsed.
try-it-requestTryItRequestWhen the user clicks "Try it" — payload of the outgoing request.
try-it-responseTryItResponseWhen the Try-It response is received.
el.addEventListener('spec-loaded', (e) => {
console.log((e as CustomEvent).detail) // { title, version, type }
})

Shadow DOM

The element uses open shadow DOM. You can inspect it via el.shadowRoot for debugging. The renderer's styles are isolated from your page — host page CSS does not leak in, and renderer styles do not leak out.

Design tokens cascade in

CSS custom properties inherit through shadow boundaries, so design tokens defined on the host element are picked up by the renderer:

omnispec-renderer {
--omnispec-color-primary: #8B5CF6;
--omnispec-color-primary-hover: #7C3AED;
--omnispec-font-sans: 'Inter', sans-serif;
}

See Theming for the full token list.

Sizing the element

By default the element renders as display: block with width: 100% and height: 100%. Position it within your layout like any other block-level element:

omnispec-renderer {
height: 100vh;
}

Framework integration

Vanilla HTML

<script src="https://unpkg.com/@apiboost/omnispec@latest/dist/wc/standalone.js"></script>
<omnispec-renderer spec-url="/openapi.json"></omnispec-renderer>

Vue 3

Tell the Vue compiler that any <omnispec-...> tag is a custom element so it does not try to resolve it as a Vue component:

// vite.config.ts
import vue from '@vitejs/plugin-vue'

export default defineConfig({
plugins: [vue({
template: {
compilerOptions: {
isCustomElement: (tag) => tag.startsWith('omnispec-'),
},
},
})],
})
<script setup lang="ts">
import '@apiboost/omnispec/wc'
import { ref, onMounted } from 'vue'
import type { OmniSpecRendererElement } from '@apiboost/omnispec/wc'

const docs = ref<OmniSpecRendererElement | null>(null)
onMounted(() => {
if (docs.value) docs.value.theme = { base: 'dark' }
})
</script>

<template>
<omnispec-renderer
ref="docs"
spec-url="/openapi.json"
@spec-loaded="console.log($event.detail)"
></omnispec-renderer>
</template>

Angular

import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
import '@apiboost/omnispec/wc'

@Component({
standalone: true,
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `<omnispec-renderer spec-url="/openapi.json"></omnispec-renderer>`,
})
export class AppComponent {}

Svelte 5

<script lang="ts">
import '@apiboost/omnispec/wc'
import type { OmniSpecRendererElement } from '@apiboost/omnispec/wc'

let docs: OmniSpecRendererElement | undefined = $state()
$effect(() => {
if (docs) docs.theme = { base: 'dark' }
})
</script>

<omnispec-renderer
bind:this={docs}
spec-url="/openapi.json"
></omnispec-renderer>

React

If you already use React, prefer the React component — there is no benefit to wrapping it in a custom element. The <omnispec-renderer> element exists for non-React stacks.

Pro features in the Web Component

Pro

Pro renderers (GraphQL, SOAP/WSDL, gRPC), full white-label theming (theme.overrides), and interactive OAuth require Apiboost OmniSpec Pro. In the free core, the <omnispec-renderer> element renders OpenAPI and AsyncAPI specs with the base light/dark/auto theme and CSS-variable overrides. A parallel Pro Web Component is not shipped today — Pro features are available through the React component.

Limitations and future work

  • JSON attributes are convenient but verbose. Prefer the imperative property API for production use.
  • @emotion/css style mirroring copies emotion-generated styles from document.head into the shadow root. The footprint per page is tiny (typically a few kilobytes), but if you mount many <omnispec-renderer> elements on one page they will each carry a copy. For most users this is not a concern.