Resources

type RemoteDataState<T> = "loading" | ["data", T] | ["error", unknown]; export function RemoteData<T>(url: string) { return Resource((resource): Reactive<RemoteDataState<T>> => { const result = cell("loading" as RemoteDataState<T>); const controller = new AbortController(); resource.on.cleanup(() => controller.abort()); fetch(url, { signal: controller.signal }) .then((response) => response.json() as Promise<T>) .then((data) => { result.set(["data", data]); }) .catch((error) => { result.set(["error", error]); }); return result; }); }

In UI Frameworks

React

import { use } from "@starbeam/react"; import { RemoteData } from "./remote-data.js"; export default function UserCard({ username }: { username: string }) { const user = use( () => RemoteData(`https://api.github.com/users/${username}`), [username] ); switch (user.type) { case "loading": return <div>Loading...</div>; case "data": return ( <div> <img src={user.data.avatar_url} /> <h1>{user.data.name}</h1> <p>{user.data.bio}</p> </div> ); case "error": return <div>Error: {user.error.message}</div>; } }

Svelte

<script lang="typescript"> import { use } from "@starbeam/svelte"; import { RemoteData } from "./remote-data.js"; export let username: string; $: user = use(RemoteData(`https://api.github.com/users/${username}`)); </script> {#if user.type === "loading"} <div>Loading...</div> {:else if user.type === "data"} <div> <img src={user.data.avatar_url} /> <h1>{user.data.name}</h1> <p>{user.data.bio}</p> </div> {:else if user.type === "error"} <div>Error: {user.error.message}</div> {/if}

Vue

<script setup lang="ts"> import { use } from "@starbeam/vue"; import { RemoteData } from "./remote-data.js"; const props = defineProps<{ username: string }>(); const user = use(() => RemoteData(`https://api.github.com/users/${props.username}`)); defineExpose({ user }); </script> <template> <div v-if="user.type === 'loading'">Loading...</div> <div v-if="user.type === 'data'"> <img :src="user.data.avatar_url" /> <h1>{user.data.name}</h1> <p>{user.data.bio}</p> </div> <div v-if="user.type === 'error'">Error: {user.error.message}</div> </template>

Ember

import { use } from "@starbeam/ember"; import { LiterCounter } from "#reactive/liter-counter"; export default class LiterLikeButton extends Component { readonly liters = use(LiterCounter); <template> <button {{on "click" this.liters.increment}}>Add a liter</button> <p>{this.liters.description}</p> </template> }