Fetching data with a loading state
The screen requests a list of users, shows a status while the request is in flight, and fills a scrollable list from the response. fetch is async, parsing the body is not: res.json() hands back the data directly. Only the UIScrollable scrolls — and it is what gives you pull-to-refresh.
type User = { id: number; name: string; email: string }
let statusText: UIText
let list: UIColumn
const loadData = async () => {
statusText.text = "Loading..."
const res = await fetch("https://jsonplaceholder.typicode.com/users")
if (res.status !== 200) {
statusText.text = "Error loading data"
return
}
const users = res.json<User[]>() // sync — no await
statusText.text = ""
list.setContent(
users.map(user =>
UIColumn([
UIText(user.name).style({ fontWeight: 700, color: "white" }),
UIText(user.email).style({ color: "#888", fontSize: 13 })
]).style({ px: 16, py: 12, gap: 4 })
)
)
}
const screen = UIScreen([
UIText("Users").style({ fontSize: 24, fontWeight: 700, color: "white", mb: 8 }),
statusText = UIText("").style({ color: "#888" }),
UIScrollable([
list = UIColumn([])
]).style({ flexGrow: 1 }) // the ONE scrolling body — the screen itself never scrolls
.onRefresh(() => loadData()) // pull-to-refresh; attached before screen.open()
])
.style({ bgColor: "black", p: 16, pt: "max(safe-top, 24px)" })
.onOpen(() => loadData())
screen.open()Press Run to launch the floating simulator. It stays open as you browse — opening another example just swaps the app inside it.