API reference › @evolu/common › Type › Url
const Url: brand(
"Url",
String,
(value: string) =>
canonicalizeUrl(value) === value
? ok()
: err<UrlError>({ type: "Url", value }),
formatUrlError,
);
Defined in: packages/common/src/Type.ts:9709
Canonical absolute URL String.
A Url is a URL as the WHATWG URL Standard
serializes it, so new URL(value).href equals the value, such as
https://example.com/ for https://example.com. Like DateIso, it has
one spelling per URL on an engine, so equal URLs are equal strings there. Url
does not normalize; use UrlFromString to convert other spellings, such
as user input. Relative URLs are rejected.
Url accepts any scheme, including javascript:, whose links run code when
followed. Use HttpUrl for links. Url sets no maximum length, and a
data: URL can hold a whole file; compose maxLength(2048)(Url) or another
limit when one matters, such as for a database column.
Url validates with the platform's URL, which browsers and Node.js provide,
and which Expo SDK 58 and later installs on React Native. React Native
without Expo has its own URL, which accepts strings that are not URLs, so
install one that follows the standard, such as
whatwg-url-minimum 0.2 or
later, which Expo installs, as the react-native
example
does without replacing React Native's URL.createObjectURL.
Engines don't always parse a URL the same way. Chromium percent-encodes |
in a path, which the others keep, and accepts a space in a host, which they
reject. Firefox rejects some xn-- hosts that the others accept, and
whatwg-url-minimum accepts some right-to-left hosts that the others reject.
Revisions of the standard change the serialization too, as when it began to
percent-encode ^ in a path in 2025. No engine decodes a percent-escape, so
the same URL can be two different strings, and a Url from one device can fail
validation on another device or after an engine update. Evolu does not
validate the values other devices write, so where a valid Url matters,
transform a value read from the database with UrlFromString and handle its
error. It doesn't make another engine's spelling equal to this one's.
Example
import { assertErr, assertOk, Url } from "@evolu/common";
assertOk(Url.fromUnknown("https://example.com/"), "https://example.com/");
assertErr(Url.fromUnknown("https://example.com"), {
type: "Url",
value: "https://example.com",
});