# Basics

{% hint style="warning" %}
Difficulty: Moderate

* Some basic knowledge of reactive UIs and state management is recommended.
  {% endhint %}

## What is $ui.register?

You can interact with the user interface and execute business logic via APIs provided to your plugin when you register a UI context.

```typescript
function init() {
   // This function registers the UI context for your plugin, allowing it to
   // have access to UI APIs
   $ui.register((ctx) => {
      // The 'ctx' objects contains all the APIs
   })
}
```

Unlike hooks which are called every time a specific event is triggered, the function inside `$ui.register` is called only once during the lifetime of your plugin, right after `init(),` in other words, each time your plugin is loaded.

{% hint style="info" %}
You cannot register hook handlers inside the UI callback.
{% endhint %}

## Fetch

{% hint style="info" %}
As of v3.3.0, network requests require you to whitelist domains [Permissions](/seanime-extensions/plugins/permissions.md#network-requests)
{% endhint %}

{% hint style="warning" %}
In the UI context, `ctx.fetch` should be used instead of simply `fetch` .
{% endhint %}

```typescript
$ui.register(async (ctx) => {
    const res = await ctx.fetch("https://jsonplaceholder.typicode.com/todos/1")
    const data = res.json()
})
```

## States

State management allows you to keep track of dynamic data within your plugin. This approach not only helps maintain a clear separation of concerns but also enables reactive programming, where UI components like the `Tray` automatically update in response to changes in states.

<pre class="language-typescript"><code class="lang-typescript">//...

<strong>const count = ctx.state(0)
</strong>
ctx.setInterval(() => {
<strong>    count.set(c => c+1)
</strong>}, 1000)

function resetCount() {
<strong>    count.set(0)
</strong>}

// Tray will update each time count changes
<strong>tray.render(() => tray.text(`Count: ${count.get()}`))
</strong></code></pre>

### Computed

```typescript
const count = ctx.state(0)
const text = ctx.computed(() => `Count is ${count.get()}`, [count])
text.get()
```

### Effects

<pre class="language-typescript"><code class="lang-typescript">// Effect registers a callback that runs each time count changes
ctx.effect(() => {
    console.log("count changed, " + count.get())
<strong>}, [count])
</strong></code></pre>

### Example

In this example, we fetch some info from an external API each time the user navigates to an anime page.

{% code title="Example" %}

```typescript
const currentMediaId = ctx.state<number | null>(null)
const fetchedData = ctx.state([])

// When the user navigates to an anime, get the media ID
ctx.screen.onNavigate((e) => {
    if (e.pathname === "/entry" && !!e.searchParams.id) {
        const id = parseInt(e.searchParams.id);
        currentMediaId.set(id);
    } else {
        currentMediaId.set(null);
    }
});

// Trigger 'ctx.screen.onNavigate' when the plugin loads 
ctx.screen.loadCurrent()

// Fetch data each time the media ID changes.
ctx.effect(async () => {
    if (!currentMediaId.get()) return
    
    const res = ctx.fetch(`https://example.com/anilistId?=${currentMediaId.get()}`)
    // Store the results
    fetchedData.set(res.json())
}, [currentMediaId])
```

{% endcode %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://seanime.gitbook.io/seanime-extensions/plugins/ui/basics.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
