Skip to content
Meeko
Type to search the guides and the API reference. Open it from any page with / or Ctrl K; move with the arrow keys and open a result with Enter.
Get started

Contents

On this page

useSocketChannel

Opens a channel by name from a Socket contract registry, bound to the component; this is the function used to generate the useSocket auto-import.

composable Since 1.0.0 @meekojs/meeko/framework Source code
ts
import { useSocketChannel } from "@meekojs/meeko/framework";

useSocketChannel(contratos, nombre, opciones?) constructs the URL for channel nombre (/_meeko/socket/<nombre>, with ?room= if there is a roomId) and opens a component-bound client with that channel's contract, like useSocketClient. Generated clients use it to define the useSocket auto-import:

ts
export const useSocket = (name, options) => useSocketChannel(socketContracts, name, options);

In a framework application, use useSocket. useSocketChannel is for a custom host or package with its own contract registry (socketContracts) that wants the same behavior. Import it from @meekojs/meeko/framework.

Usage#

ts
// src/socket.ts, in a custom host
import { useSocketChannel } from "@meekojs/meeko/framework";
import type { SocketRegistryOptions } from "@meekojs/meeko/framework";
import { socketContracts } from "./socket-contracts.js";

export const useChannel = <K extends keyof typeof socketContracts & string>(name: K, options?: SocketRegistryOptions) =>
  useSocketChannel(socketContracts, name, options);
meeko
<script lang="ts">
import { useChannel } from "../socket";
const prices = useChannel("prices", { roomId: "eur" });
prices.on("price", (price) => console.log(price.symbol, price.value));
</script>

Type#

ts
function useSocketChannel<
  C extends Record<string, SocketContract>,
  K extends keyof C & string,
>(contracts: C, name: K, options?: SocketRegistryOptions): SocketClient<C[K]>

SocketClient

ts
interface SocketClient<C extends SocketContract> {
  readonly state: ReadonlySignal<SocketClientState>;
  emit<K extends keyof C["clientToServer"] & string>(
    event: K,
    data: SchemaInput<C["clientToServer"][K]>,
  ): Promise<void>;
  on<K extends keyof C["serverToClient"] & string>(
    event: K,
    listener: (data: SchemaOutput<C["serverToClient"][K]>) => void,
  ): () => void;
  close(): void;
}

SocketRegistryOptions

ts
interface SocketRegistryOptions extends SocketClientOptions {
	readonly baseUrl?: string
	readonly pathnamePrefix?: string
	readonly roomId?: string
}

Parameters#

Parameter Type Default Description
contracts Record<string, SocketContract> required Public contracts by channel name, like the socketContracts generated by Meeko in .meeko/generated/socket-contracts.ts.
name key of contracts required Channel to open. A name not in the registry is a type error and, at runtime, TypeError: Unknown socket channel: <nombre>.
options SocketRegistryOptions {} roomId, baseUrl (the browser origin by default), pathnamePrefix ("/_meeko/socket"), retryMs, maxRetryMs, maxMessageBytes, webSocketFactory and onError.

Return value#

The SocketClient for contract contracts[name]: state, emit, on and close, closed when the component unmounts. In SSR and HTML-only rendering, it returns an inert client that does not connect: it stays in "connecting", on registers nothing and emit rejects. Through 1.6, SSR threw Error: useSocketClient() is only available in the browser.

Examples#

A package's channel registry#

ts
// packages/live/src/index.ts
import { useSocketChannel } from "@meekojs/meeko/framework";
import type { SocketRegistryOptions } from "@meekojs/meeko/framework";
import { liveContracts } from "./contracts.js";

/** Open a live service channel with its server URL. */
export function useLive<K extends keyof typeof liveContracts & string>(name: K, options: SocketRegistryOptions = {}) {
  return useSocketChannel(liveContracts, name, { baseUrl: "wss://live.example.com", ...options });
}

Choosing the channel at runtime#

With a name received as text, check it first: TypeScript no longer can.

ts
import { useSocketChannel } from "@meekojs/meeko/framework";

declare const socketContracts: Record<"chat" | "presence", Parameters<typeof useSocketChannel>[0][string]>;

function isChannel(name: string): name is "chat" | "presence" {
  return Object.hasOwn(socketContracts, name);
}

export function openChannel(name: string) {
  if (!isChannel(name)) throw new TypeError(`Canal desconocido: ${name}`);
  return useSocketChannel(socketContracts, name);
}

Common mistakes#

A channel not in the registry#

Incorrectts
const feed = useSocketChannel(socketContracts, "feeds" as never);

At runtime, it throws TypeError: Unknown socket channel: feeds. With a literal name, it is a type error; do not hide it with a cast:

Correctts
const feed = useSocketChannel(socketContracts, "feed");

Waiting for the connection with await#

Incorrectts
// In the <script> of a component
const chat = await useSocketChannel(socketContracts, "chat");

The compiler only splits setup at an await of useElementRef, useSocket or useSocketClient: this one is MEEK055. Without await, the call also works in SSR (since 1.7, it returns an inert client there; through 1.6, it threw useSocketClient() is only available in the browser). To continue setup with the socket open, use the useSocket auto-import with await:

Correctts
const chat = await useSocket("chat");

Using it outside a component#

Incorrectts
// app/utils/live.ts, evaluated on import
export const chat = useSocketChannel(socketContracts, "chat");

The client needs a component root to close on unmount: outside one, it closes immediately and throws Error: onCleanup() requires an active component root. For a standalone client you close yourself, use createSocketClient or the application's socket registry:

Correctts
import { createSocketClient } from "@meekojs/meeko/framework";
export const chat = createSocketClient("wss://example.com/_meeko/socket/chat", socketContracts.chat);

Awaiting it with await#

Incorrectts
const chat = await useSocketChannel(socketContracts, "chat");

meeko check reports MEEK055: El await de nivel superior sólo se admite sobre useElementRef, useSocket, useSocketClient, useApi, useRpcQuery; mueve el resto del trabajo asíncrono a una función o a onMounted. In an application, use the useSocket auto-import, which supports await:

Correctts
const chat = await useSocket("chat");

Notes#

  • Like useSocket, except for await. Same lifecycle (bound to the component, closed on unmount) and behavior in SSR and dry runs. The compiler only splits setup at an await of useElementRef, useSocket or useSocketClient; any other top-level await, including useSocketChannel or a custom wrapper, is MEEK055.
  • URL. baseUrl accepts http(s): and ws(s):; it defaults to the browser origin, which is what the server checks in Origin.
  • Public contracts. Contracts contain only schemas from shared/: they never import server handlers.

Was this page helpful?

Add a comment

It is sent when you choose “Yes” or “No”. Only the page, your answer and the comment are stored: no cookies and no personal data.

0 of 2000 characters

Edit on GitHub: docs/en/reference/composables/usesocketchannel.md