> ## Documentation Index
> Fetch the complete documentation index at: https://juo.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Vue

> Render blocks with Vue 3 components. Signals appear as refs through useContext.

`@juo/blocks/vue` is the Vue 3 adapter. It exposes a `createVueRenderer` for the block renderer slot and a `useContext` composable that returns service signals as Vue refs.

```sh theme={null}
pnpm add vue
```

The patterns below extend the [Blocks](/docs/blocks/concepts/blocks), [Reactivity](/docs/blocks/concepts/reactivity), and [Contexts](/docs/blocks/concepts/contexts) concept pages with Vue-specific equivalents.

## Defining a Vue block

```ts theme={null}
// Button/index.ts
import { defineBlock, registerBlock } from "@juo/blocks";
import { createVueRenderer } from "@juo/blocks/vue";
import Button from "./Button.ce.vue";

export const ButtonBlock = defineBlock("Button", {
  group: "theme",
  schema: /* ... */,
  initialValue: () => ({ props: { variant: "primary", label: "Subscribe" } }),
  renderer: createVueRenderer(Button),
});

registerBlock(ButtonBlock);
```

```vue theme={null}
<!-- Button/Button.ce.vue -->
<script setup lang="ts">
defineProps<{ variant: "primary" | "secondary"; label: string }>();
</script>

<template>
  <button :class="`btn btn--${variant}`">{{ label }}</button>
</template>
```

`createVueRenderer` mounts the component inside a `<juo-context-root>`-aware wrapper. Children rendered by Vue can call `useContext` and resolve the host's services.

## Reading services with `useContext`

`useContext` returns a proxy where every `Signal<T>` field appears as a `Ref<T>` — drop it into templates, `computed`, and `watch` like any Vue ref.

```vue theme={null}
<script setup lang="ts">
import { useContext } from "@juo/blocks/vue";
import { SubscriptionServiceContext } from "@juo/blocks";
import { computed } from "vue";

const subscription = useContext(SubscriptionServiceContext);

// subscription.current is a Ref<Subscription | null>
const status = computed(() => subscription.current.value?.status ?? "unknown");

async function pause() {
  const id = subscription.current.value?.id;
  if (!id) return;
  await subscription.pause(id);
}
</script>

<template>
  <p>Status: {{ status }}</p>
  <button @click="pause">Pause</button>
</template>
```

The `$` accessor returns the underlying signal when direct signal access is required:

```ts theme={null}
const subscription = useContext(SubscriptionServiceContext);
const raw = subscription.$.current; // Signal<Subscription | null>
```

## Providing contexts from Vue

Use `useProvideContext` inside a wrapping component:

```vue theme={null}
<script setup lang="ts">
import { useProvideContext } from "@juo/blocks/vue";
import { CartContext, createCart } from "./cart";

useProvideContext(CartContext, createCart());
</script>
```

## Bridging Vue refs and signals

The `@juo/blocks/vue` entry also exposes `toSignal` and `toRef` for the rare cases where crossing the boundary manually is needed:

```ts theme={null}
import { toSignal, toRef } from "@juo/blocks/vue";
import { ref } from "vue";

const myRef = ref(0);
const myRefAsSignal = toSignal(myRef);    // Signal<number>

import { signal } from "@juo/blocks";
const mySignal = signal("hello");
const mySignalAsRef = toRef(mySignal);    // Ref<string>
```

These utilities are available for cases where crossing the boundary manually is required. The `useContext` proxy returns refs for most use cases.
