LeCodesdocs

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.

Fork in LeCodes
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.

Related docs