In this page
SDK Web
Introduction
GreyCat’s Web SDK is available here @greycat/web and features:
- Built on top of the Web Component standards and Shoelace
- Tight integration with GreyCat
stdlibrary - Provides several production-ready components for: charting, forms, virtual tables, etc.
Project Setup
Scaffold a new project with VitePlus+:
vp create github:datathings/greycat-web-template
cd your-project
vp run setup
vp install
vp run setup fetches the latest GreyCat core and @greycat/web versions automatically.
The template uses the greycat() VitePlus+ plugin (in vite.config.ts) to configure multi-page app support, relative URLs, and API proxying to the GreyCat backend.
JSX is optional.
@greycat/webworks with plaindocument.createElement()or JSX — pick whichever fits the situation. See the Advanced Web App tutorial for examples of both.
How it works
When importing @greycat/web every Web Component that it defines will then be registered globally
as Custom Elements.
Because some of those components communicate with GreyCat internally, we need to tell them where
to find the current instance. For this, the @greycat/sdk and more generally @greycat/web leverage global scope.
All the functions and components that need to communicate directly with GreyCat will look for a GreyCat instance at globalThis.greycat.default.
This implies that the only “initialization” that is needed is the following:
import "@greycat/web";
await gc.sdk.init();
// here you can safely use all of @greycat/web
Authentication
gc.sdk.init() accepts an optional auth option that is resolved before the ABI is downloaded (the ABI endpoint is itself access-controlled, so authentication has to happen first). The form you pass determines both how requests are authenticated and the shape of the value init returns.
auth |
Mechanism | init resolves to |
|---|---|---|
| (omitted) | anonymous | GreyCat |
{ username, password } |
runtime::Identity::login, then a bearer token |
GreyCat |
{ token } |
a pre-obtained bearer token | GreyCat |
{ openid: <provider> } |
server-driven OpenID Connect (recommended) | Ready | Redirecting |
{ openidPkce: <provider> } |
client-driven OpenID Connect (PKCE) | Ready | Redirecting |
an AuthStrategy |
a custom or built-in strategy | Ready | Redirecting |
Credentials and tokens
const greycat = await gc.sdk.init({ auth: { username: "alice", password: "..." } });
// or, with an existing token
const greycat = await gc.sdk.init({ auth: { token } });
These return a GreyCat instance directly, exactly like an unauthenticated init().
OpenID Connect
For OpenID Connect, init runs the full browser redirect flow for you: it finishes an in-flight provider callback, reuses an existing session, or starts a login by navigating to the provider. Because the page may leave for the identity provider, init resolves to either a Ready result or a Redirecting marker. Narrow the two with gc.sdk.isRedirecting:
import "@greycat/web";
const r = await gc.sdk.init({ auth: { openid: "keycloak" } });
if (gc.sdk.isRedirecting(r)) {
// First visit: the browser is navigating to the identity provider.
// Nothing after this line runs.
} else {
const { greycat, auth } = r;
// Signed in. The session is carried by a cookie.
// `auth?.returnTo` is where the original login asked to come back to (or null).
}
The provider ('keycloak' above) must be registered server-side in your GreyCat project with the openid library:
@library("openid", "8.0.232-dev"); // use a version matching your GreyCat core
fn main() {
Openid::register("keycloak", OidcProvider {
issuer: "https://idp.example.com/realms/myrealm",
client_id: "greycat",
client_secret: System::getEnv("KC_CLIENT_SECRET"),
redirect_uri: "https://app.example.com/",
auto_create_identity: true,
});
}
With { openid } (recommended), GreyCat brokers the OAuth exchange server-side using the client secret; the browser only follows redirects and no token is exposed to the page. Use { openidPkce } instead for a public client, where GreyCat holds no secret and the browser performs the token exchange itself:
const r = await gc.sdk.init({ auth: { openidPkce: "keycloak" } });
To sign out, clear the GreyCat session and restart the flow:
await gc.sdk.logout();
Custom strategies
{ openid } and { openidPkce } are shorthands for the built-in strategy factories gc.sdk.openidServerAuth() and gc.sdk.openidPkceAuth() (alongside gc.sdk.passwordAuth() and gc.sdk.tokenAuth()). Any value implementing the AuthStrategy interface can be passed to auth, which is also how you supply per-call overrides:
const r = await gc.sdk.init({
auth: gc.sdk.openidServerAuth({ provider: "keycloak", returnTo: "/dashboard" }),
});
Components
We offer a range of web components within the web SDK. Please check the Web Components section for further details or use the list below to jump directly to the various components.
Global Timezone
To define the global timezone used by all WebComponents set it at initialization:
import "@greycat/web";
// at initialization
const greycat = await gc.sdk.init({ timezone: "Europe/Luxembourg" });
// or at runtime
greycat.timezone = gc.core.TimeZone["Europe/Luxembourg"];
Formatting (Numbers)
All GUI elements leverage the same instance of Intl for the display of dates and numbers
If you wish to override them, you can initialize GreyCat with a specific formatter or modify it at runtime on the GreyCat instance:
// at initialization
const greycat = await gc.sdk.init({
numFmt: new Intl.NumberFormat("de-DE", { maximumFractionDigits: 2 }),
});
// or at runtime
greycat.numFmt = new Intl.NumberFormat("de-DE", { maximumFractionDigits: 2 });
Shoelace
Underneath, most of our components leverage Shoelace components. Specifically, all the gui-input components use sl-input. If you wish to style them, refer to the Shoelace documentation.
By default, @greycat/web also comes with all Shoelace components already imported. You don’t require any extra configuration; once our library is imported, you are free to use any of Shoelace’s components.
You can access Shoelace by importing sl from @greycat/web, in case you need to change internal configuration, for example to set a custom icon library.
import { sl } from "@greycat/web";
sl.registerIconLibrary("default", {
resolver: (name) => `https://cdn.jsdelivr.net/npm/bootstrap-icons@1.0.0/icons/${name}.svg`,
});
d3
The d3.js library is also exposed
import { d3 } from "@greycat/web";
Maplibregl
GreyCat comes with a component gui-map that relies on maplibre-gl in order to work. This library is not vendored with @greycat/web, so you must
ensure that maplibregl is globally available prior to initializing the SDK:
import "@greycat/web";
import maplibregl from "maplibre-gl";
await gc.sdk.init({ maplibregl });
Running a task and waiting immediately
You can execute a task and wait for its result in a single step using spawnAwait.
This is the simplest way to run a task and immediately retrieve its output.
import "@greycat/web";
const greycat = await gc.sdk.init();
// spawn a task and wait for its result
const res = await greycat.spawnAwait("project::sum", [40, 2]);
// res = 42
This pattern is useful when you don’t need to interact with the task between its creation and completion.
Running a task and waiting later
Alternatively, you can spawn a task without immediately waiting for it to complete. This gives you more control over when the result is retrieved.
import "@greycat/web";
const greycat = await gc.sdk.init();
// spawn the task (returns a handle immediately)
const task = await greycat.spawn("project::sum", [2, 40]);
// ...do other work while the task runs in the background...
// wait for the task to complete and get the result
const res = await task.await();
// res = 42
This pattern is ideal when you want to queue multiple tasks or overlap task execution with other work.