Extensions
Install CLI
Install the Elements CLI to your system. This will add the nve command to your path and provide several helpful commands for working with Elements.
curl -fsSL https://nvidia.github.io/elements/install.sh | bash
curl -fsSL https://nvidia.github.io/elements/install.cmd -o install.cmd && install.cmd && del install.cmd
# install the CLI
npm install -g @nvidia-elements/cli
Create a New Project
Use the Elements CLI to quickly bootstrap a new extensions project with the necessary dependencies:
nve project.create --type=extensions
Setup an Existing Project
Setup an existing project to use Elements you can use the setup command to add the necessary dependencies and configure the MCP server.
nve project.setup
If not yet done, install NodeJS. NodeJS is a JavaScript runtime that has a large ecosystem of tooling and packages for Web Development. Once installed the Node Package Manager (NPM) will be available for use.
Manual Integration
If installing to an existing project, install the core dependencies:
# install core dependencies
npm install @nvidia-elements/themes @nvidia-elements/styles @nvidia-elements/core
To create reusable UI components that build on top of Elements, consider using lit.dev for authoring highly reusable custom elements (Web Components). This path enables your extensions to work in a large variety of frameworks and environments. Read the publishing and best practices provided by the lit team. The rest of this guide focuses on how to integrate specifically for Element integration and best practices.
Scoped Registry
By default Web Components, specifically the custom elements spec, defines elements on a global registry. This can introduce tag name collisions if the browser loads many versions of the same library. To avoid this, use a scoped registry. This allows you to define your own registry and ensure the Elements you depend on are only registered to the scope of your component and not the global registry.
import { html, LitElement } from 'lit';
import { customElement } from 'lit/decorators/custom-element.js';
import { Input } from '@nvidia-elements/core/input';
import { Password } from '@nvidia-elements/core/password';
import { Button } from '@nvidia-elements/core/button';
const libraryRegistry =
globalThis.CustomElementRegistry && 'initialize' in CustomElementRegistry.prototype
? new CustomElementRegistry()
: customElements;
@customElement('domain-login')
export class DomainLogin extends LitElement {
static shadowRootOptions = {
...LitElement.shadowRootOptions,
customElementRegistry: libraryRegistry
};
constructor() {
super();
libraryRegistry.get('domain-login') || libraryRegistry.define('domain-login', DomainLogin);
libraryRegistry.get('nve-input') || libraryRegistry.define('nve-input', Input);
libraryRegistry.get('nve-password') || libraryRegistry.define('nve-password', Password);
libraryRegistry.get('nve-button') || libraryRegistry.define('nve-button', Button);
}
render() {
return html`
<nve-input>
<label>Email</label>
<input type="email" />
</nve-input>
<nve-password>
<label>Password</label>
<input type="password" />
</nve-password>
<nve-button interaction="emphasis">Login</nve-button>
`;
}
}
// define.ts
import { DomainLogin } from './login.js';
// register the component globally for your users to import and consume
customElements.get('domain-login') || customElements.define('domain-login', DomainLogin);
declare global {
interface HTMLElementTagNameMap {
['domain-login']: DomainLogin;
}
}
// internal/index.ts
// shared private registry for all of the library sub dependencies/components
export const libraryRegistry =
globalThis.CustomElementRegistry && 'initialize' in CustomElementRegistry.prototype
? new CustomElementRegistry()
: customElements;
export function define(
tag: string,
element: CustomElementConstructor,
registry: CustomElementRegistry = customElements
) {
registry.get(tag) || registry.define(tag, element);
}