public-ip

Fetches your public IP from a list of HTTP endpoints and exposes it as global atoms, with stale-while-revalidate caching. publicIpAtom resolves to the IP (Promise<string> on first read, then string).

Async atom
publicIpAtom suspends on first read. Wrap consumers in <Suspense>, or read the non-suspending publicIpValueAtom / publicIpStatusAtom instead.

Install

bun add @valdres/public-ip

Live example

Loading demo…

Usage

import { Suspense } from "react"
import { useValue } from "valdres-react"
import { publicIpAtom } from "@valdres/public-ip"

function Ip() {
    const ip = useValue(publicIpAtom) // string (suspends until resolved)
    return <span>{ip}</span>
}

function App() {
    return (
        <Suspense fallback="Loading…">
            <Ip />
        </Suspense>
    )
}

Exports

ExportKindType
publicIpAtomatom (read-only)Promise<string> | string
publicIpStatusAtomatom (read-only)PublicIpStatus
publicIpErrorAtomatom (read-only)Error | null
publicIpValueAtomatom (read-only)string | null
publicIpV4Atomatom (read-only)Promise<string> | string
publicIpV4StatusAtomatom (read-only)PublicIpStatus
publicIpV4ErrorAtomatom (read-only)Error | null
publicIpV4ValueAtomatom (read-only)string | null
publicIpV6Atomatom (read-only)Promise<string> | string
publicIpV6StatusAtomatom (read-only)PublicIpStatus
publicIpV6ErrorAtomatom (read-only)Error | null
publicIpV6ValueAtomatom (read-only)string | null
publicIpEndpointsAtomatom (settable)string[]
publicIpV4EndpointsAtomatom (settable)string[]
publicIpV6EndpointsAtomatom (settable)string[]
publicIpMaxAgeAtomatom (settable)number (ms)
publicIpStaleWhileRevalidateAtomatom (settable)number (ms)
publicIpStaleIfErrorAtomatom (settable)number (ms)
fetchPublicIputil fn(endpoints: string[], validate?: (v: string) => boolean, timeoutMs?: number) => Promise<string>
PublicIpStatustype"idle" | "loading" | "revalidating" | "ok" | "error"

The V4 / V6 atoms are the same shape, fetching from IPv4- and IPv6-only endpoints. Endpoint, maxAge, staleWhileRevalidate, and staleIfError atoms are settable to tune fetching.

Cross-framework

Atoms are global: the fetch starts on the first subscriber and the result is shared across every store. Only the read primitive changes per framework (useValue, createValue, injectValue, watch, or store.get / store.sub in plain JS).