# Components
In SUI, **every page is a component**. Any page can be embedded into another page using the `is` attribute.
## Core Concept
When a page is used as a component:
1. The page's HTML becomes the component template
2. The page's CSS is automatically scoped
3. The page's TypeScript becomes the component class
4. The page's `backend.ts` provides server-side logic via `BeforeRender`
## Creating a Component
A component is just a page with a single root element:
**`/card/card.html`**:
```html
{{ title }}
```
**`/card/card.css`**:
```css
.card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 16px;
}
.card h3 {
margin: 0 0 12px;
}
```
**`/card/card.ts`**:
```typescript
import { Component } from "@yao/sui";
const self = this as Component;
// self.root - Root element
// self.store - Data store
// self.props - Props from attributes
```
## Using Components
### Basic Usage
Use the `is` attribute to embed a page as a component:
```html
Card content goes here
```
### With Import Alias
Use `` for cleaner syntax:
```html
Content
```
## Props
Props are passed as attributes:
```html
```
Access props in the component script:
```typescript
import { Component } from "@yao/sui";
const self = this as Component;
// Get single prop
const name = self.props.Get("name");
// Get all props
const allProps = self.props.List();
// { name: "John", email: "john@example.com", avatar: "...", role: "admin" }
```
Access props in backend script:
```typescript
function BeforeRender(
request: Request,
props: Record
): Record {
const userId = props.userId;
return {
user: Process("models.user.Find", userId),
};
}
```
## Children and Slots
### Children
Use `` to render child content:
**Component (`/panel/panel.html`)**:
```html
{{ title }}
```
**Usage**:
```html
This content appears in the panel body
```
### Named Slots
Use `` for multiple content areas:
**Component (`/modal/modal.html`)**:
```html
```
**Usage**:
```html
Confirmation
Are you sure you want to proceed?
```
## Dynamic Components
### Variable Component Route
```html
```
### Dynamic Tag
```html
```
## Component Script
### Structure
```typescript
import { $Backend, Component, EventData } from "@yao/sui";
const self = this as Component;
// self.root - Root element (HTMLElement)
// self.store - Data store (data-* attributes)
// self.props - Props (passed attributes)
// self.state - State management
// State watchers
self.watch = {
propertyName: (value: any, state: any) => {
// React to state changes
},
};
// Event handlers (bound to s:on-click="HandleClick")
self.HandleClick = async (event: Event, data: EventData) => {
const result = await $Backend().Call("Method", data.id);
// Handle result
};
```
### Store API
```typescript
import { Component } from "@yao/sui";
const self = this as Component;
// String data
self.store.Get("key");
self.store.Set("key", "value");
// JSON data
self.store.GetJSON("items");
self.store.SetJSON("items", [{ id: 1 }]);
// Component data (from BeforeRender)
self.store.GetData();
```
### Props API
```typescript
// Get single prop
const value = self.props.Get("propName");
// Get all props
const props = self.props.List();
```
### State API
```typescript
// Set state (triggers watchers)
self.state.Set("count", 10);
// Watch state changes
self.watch = {
count: (value: number, state: any) => {
self.root.querySelector(".count")!.textContent = String(value);
// state.stopPropagation(); // Prevent bubbling to parent
},
};
```
## Nested Components
Components can include other components:
```html
```
## Component Backend Script
**`/user-card/user-card.backend.ts`**:
```typescript
function BeforeRender(
request: Request,
props: Record
): Record {
const userId = props.userId;
return {
user: Process("models.user.Find", userId),
permissions: Process("scripts.auth.GetPermissions", userId),
};
}
function ApiUpdateUser(userId: string, data: any, request: Request): any {
return Process("models.user.Save", userId, data);
}
```
## CSS Scoping
Component CSS is automatically scoped using namespace attributes:
**Original CSS**:
```css
.card {
border: 1px solid #ddd;
}
.card h3 {
color: #333;
}
```
**Compiled CSS** (scoped):
```css
[s:ns="ns_abc123"] .card {
border: 1px solid #ddd;
}
[s:ns="ns_abc123"] .card h3 {
color: #333;
}
```
## Important Notes
1. **Single Root Element**: Components must have exactly one root element
2. **Scoped Styles**: CSS is automatically scoped to prevent conflicts
3. **Recursive Prevention**: SUI detects and prevents recursive component inclusion
4. **Component Pattern**: Use `const self = this as Component` to access component APIs