Building UI Components
Rendering entities as visual interfaces in the IDE.
When RARS works with your domain, the data lives as entities in the knowledge graph. UI components give those entities a visual representation. A Person entity becomes a profile card. A WorkOrder becomes a detail view with status, assignee, and history. Components are how your operating model becomes something you can see and interact with.
This guide covers how to think about the component model.
Server Components Only
All third-party UI components are React Server Components (RSC). This is a deliberate security decision: components run on the server, produce serialized output, and stream to the client. No untrusted JavaScript executes in your browser.
This means:
- No hooks (
useState,useEffect,useRef) - No event handlers (
onClick,onSubmit,onChange) - No client-side state or lifecycle
- No
"use client"directive
Your components receive props and return JSX. That's it. For interactive elements (buttons, inputs, forms, dialogs), use the pre-built components from @poliglot/ui, which are part of the trusted platform codebase.
Your components receive props and return JSX. That's it. For interactive elements (buttons, inputs, forms, dialogs), use the pre-built components from @poliglot/ui, which are part of the trusted platform codebase.
import { Card, Badge } from "@poliglot/ui";
interface PersonCardProps {
name: string;
role: string;
active: boolean;
}
export function PersonCard({ name, role, active }: PersonCardProps) {
return (
<Card className="p-4 rounded-lg bg-card">
<h3 className="text-lg font-semibold text-foreground">{name}</h3>
<p className="text-sm text-muted-foreground">{role}</p>
<Badge variant={active ? "default" : "secondary"}>
{active ? "Active" : "Inactive"}
</Badge>
</Card>
);
}For now, UI components are strictly visual interfaces. We are in the process of designing the system for you to manually execute actions against the operating system, but for now everything is driven by RARS.
The Component Model
Three concepts link your React components to your domain entities:
Bundle: the compiled JavaScript artifact containing your components. One bundle per matrix, produced by the poliglot-ui build tool.
Component: a named export from your bundle. Each component maps to an export name (e.g., PersonCard, OrderDetail).
Renderable: the binding between a component and a domain class. A renderable declares "this component renders entities of this type" and includes a SPARQL query that extracts props from the entity.
# Declare the renderable: PersonCard renders people:Person entities
people:PersonDetailView
a rars-ui:Renderable ;
rars-ui:forType people:Person ;
rars-ui:component people:PersonDetailComponent ;
rars-ui:propsQuery [
rars-os:json """
PREFIX people: <https://example.org/spec/people#>
JSON {
"name": ?name,
"email": ?email,
"department": ?department
} WHERE {
?subject people:name ?name .
OPTIONAL { ?subject people:email ?email . }
OPTIONAL { ?subject people:department ?department . }
}
"""
] .The rars-ui:propsQuery is a SPARQL JSON query that runs with ?subject bound to the entity being rendered. It extracts properties from the graph and returns a JSON object that becomes the component's props. This is how domain data flows from the graph into your React component.
How Rendering Works
When an entity needs to be rendered (either because RARS decides to show it or because you click on it), the flow is:
- RARS looks up the entity's type in the graph
- The component registry finds the most specific
rars-ui:Renderablefor that type (walking up the class hierarchy if needed) - The props query executes against the entity, producing a JSON object
- The component renders server-side in an isolated sandbox with those props
- The serialized output streams to your browser
If no renderable exists for an entity's type, RARS falls back to a default graph visualization that shows the entity's properties and relationships as an interactive network.
The @poliglot/ui Library
Your components are built on top of @poliglot/ui@poliglot/ui, a library of 50+ React components based on shadcn/ui and Radix UI primitives. These are the only external imports allowed in your components (besides React itself).
Your components are built on top of @poliglot/ui, a library of 50+ React components based on shadcn/ui and Radix UI primitives. These are the only external imports allowed in your components (besides React itself).
Available components include: Card, Badge, Button, Input, Dialog, Table, Avatar, Accordion, Tabs, Select, Form, Progress, Separator, Tooltip, and many more. See the full component reference for props, variants, and usage examples. Available components include: Card, Badge, Button, Input, Dialog, Table, Avatar, Accordion, Tabs, Select, Form, Progress, Separator, Tooltip, and many more.
All styling uses Tailwind CSS utility classes. Custom CSS files, inline styles, and CSS-in-JS are not allowed. This constraint keeps the visual language consistent across all components in a workspace, regardless of which matrix provided them.
Security Constraints
The component system enforces security through multiple layers. Understanding these constraints helps you write components that pass validation on the first try. For the complete allowlists, blocklists, and validation error types, see the UIKit constraints referenceUIKit constraints reference. The component system enforces security through multiple layers. Understanding these constraints helps you write components that pass validation on the first try.
Allowed imports: only @poliglot/ui@poliglot/ui and react. No other npm packages.
Allowed imports: only @poliglot/ui and react. No other npm packages.
Allowed HTML elements: content elements (div, span, p, headings, lists, tables, etc.) and media elements with URL validation (img, video, audio). For form elements and interactive components, use @poliglot/ui@poliglot/ui.
Allowed HTML elements: content elements (div, span, p, headings, lists, tables, etc.) and media elements with URL validation (img, video, audio). For form elements and interactive components, use @poliglot/ui.
Blocked HTML elements: script, iframe, form, input, button, textarea, select, style, link, and other elements that could execute scripts, load external content, or capture user input outside of RARS's control.
Tailwind restrictions: positioning classes (fixed, absolute, z-*), viewport-relative sizing (w-screen, h-screen), and arbitrary value syntax (e.g., bg-[...]) are blocked to prevent components from breaking out of their container.
Bundle size: 2MB maximum per matrix.
These constraints exist because components from any installed matrix run in the same workspace. A malicious or poorly written component shouldn't be able to capture credentials, overlay phishing content, or execute arbitrary code. The RSC model and validation pipeline ensure that components are pure rendering functions.
The Development Workflow
# Initialize a component project
rars init my-components
# Write your components in src/components/
# Export them from src/components/index.ts
# Build the component bundle
poliglot-ui build
# Package and install (includes components as artifacts)
rars build && rars install --workspace my-workspaceThe poliglot-ui build tool compiles your TypeScript components, validates them against the security constraints, and produces a bundle artifact. The bundle is included in your matrix package alongside the spec files.
Designing Components Around Entities
Think about components as views on your domain entities, not as standalone UI pages. Each renderable binds to a specific class in your ontology. When RARS encounters an entity of that type, your component is what you see.
A few design considerations:
- One renderable per class: RARS selects the most specific component for an entity's type. If you define a renderable for
WorkOrderand another forElectricalWorkOrder(a subclass), electrical work orders get the specialized view. - Props come from the graph: your props query determines what data the component receives. Design it to extract exactly what the component needs, including data from related entities (via SPARQL joins).
- Nested data is supported: the SPARQL JSON syntax supports nested arrays (e.g., a list of friends within a person view). Use this for related entities that should appear inline.
- Graceful handling of optional data: use
OPTIONALin your props query for properties that may not exist. Design your component to handle missing props.
Summary
- RSC only: components are pure render functions with no client-side execution
- Three-layer model: Bundle (compiled JS), Component (named export), Renderable (binds component to domain class with props query)
- Props from the graph: SPARQL JSON queries extract entity data as component props - @poliglot/ui@poliglot/ui as foundation: 50+ pre-built components, Tailwind CSS styling
- @poliglot/ui as foundation: 50+ pre-built components, Tailwind CSS styling
- Security through constraints: import restrictions, element allowlists, Tailwind validation, bundle size limits
- Type-based resolution: RARS walks the class hierarchy to find the most specific component for each entity