home / skills / workleap / wl-squide / workleap-squide
/skills/workleap-squide
npx playbooks add skill workleap/wl-squide --skill workleap-squideReview the files below or copy the command above to add this skill to your agents.
---
name: workleap-squide
description: |
Squide is a React modular application shell for Workleap web applications. Use this skill when:
(1) Creating or modifying Squide host applications or modules
(2) Registering routes, navigation items, or MSW request handlers
(3) Working with FireflyRuntime, initializeFirefly, or AppRouter
(4) Setting up integrations: TanStack Query, i18next, LaunchDarkly, Honeycomb, MSW, Storybook
(5) Implementing deferred registrations or conditional navigation items
(6) Fetching global public/protected data with usePublicDataQueries/useProtectedDataQueries
(7) Using Squide hooks: useNavigationItems, useRenderedNavigationItems, useIsBootstrapping, useLogger, useEventBusListener, useEventBusDispatcher, useEnvironmentVariable, useFeatureFlag
(8) Implementing error boundaries in modular applications
(9) Questions about modular architecture patterns in React applications
---
# Squide Framework
Squide is a React modular application shell. Use only documented APIs.
## Core Concepts
### Runtime
The `FireflyRuntime` instance is the backbone of a Squide application. Never instantiate directly - use `initializeFirefly()`.
### Modular Registration
Modules register routes, navigation items, and MSW handlers via a registration function. Each module contributes its own configuration, assembled by the host at bootstrapping.
### Public vs Protected Routes
- Routes default to `protected` (rendered under `ProtectedRoutes` placeholder)
- Use `registerPublicRoute()` for public routes (rendered under `PublicRoutes` placeholder)
- Public routes only fetch public global data; protected routes fetch both public and protected data
### Deferred Registrations
Navigation items dependent on remote data or feature flags use two-phase registration:
1. First phase: Register static routes and navigation items
2. Second phase: Return a function from registration to defer navigation items
## Quick Reference
### Host Application Setup
```tsx
// host/src/index.tsx
import { createRoot } from "react-dom/client";
import { FireflyProvider, initializeFirefly } from "@squide/firefly";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { App } from "./App.tsx";
import { registerHost } from "./register.tsx";
const runtime = initializeFirefly({
localModules: [registerHost]
});
const queryClient = new QueryClient();
const root = createRoot(document.getElementById("root")!);
root.render(
<FireflyProvider runtime={runtime}>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</FireflyProvider>
);
```
```tsx
// host/src/App.tsx
import { AppRouter, useIsBootstrapping } from "@squide/firefly";
import { createBrowserRouter, Outlet } from "react-router";
import { RouterProvider } from "react-router/dom";
function BootstrappingRoute() {
if (useIsBootstrapping()) {
return <div>Loading...</div>;
}
return <Outlet />;
}
export function App() {
return (
<AppRouter>
{({ rootRoute, registeredRoutes, routerProviderProps }) => (
<RouterProvider
router={createBrowserRouter([{
element: rootRoute,
children: [{
element: <BootstrappingRoute />,
children: registeredRoutes
}]
}])}
{...routerProviderProps}
/>
)}
</AppRouter>
);
}
```
```tsx
// host/src/register.tsx
import { PublicRoutes, ProtectedRoutes, type ModuleRegisterFunction, type FireflyRuntime } from "@squide/firefly";
import { RootLayout } from "./RootLayout.tsx";
export const registerHost: ModuleRegisterFunction<FireflyRuntime> = runtime => {
runtime.registerRoute({
element: <RootLayout />,
children: [PublicRoutes, ProtectedRoutes]
}, { hoist: true });
runtime.registerRoute({ index: true, element: <HomePage /> });
runtime.registerPublicRoute({ path: "*", element: <NotFoundPage /> });
};
```
### Local Module Setup
```tsx
// local-module/src/register.tsx
import type { ModuleRegisterFunction, FireflyRuntime } from "@squide/firefly";
import { Page } from "./Page.tsx";
export const register: ModuleRegisterFunction<FireflyRuntime> = runtime => {
runtime.registerRoute({
path: "/page",
element: <Page />
});
runtime.registerNavigationItem({
$id: "page",
$label: "Page",
to: "/page"
});
};
```
### Navigation Rendering
```tsx
import { Link, Outlet } from "react-router";
import {
useNavigationItems, useRenderedNavigationItems, isNavigationLink,
type RenderItemFunction, type RenderSectionFunction
} from "@squide/firefly";
const renderItem: RenderItemFunction = (item, key) => {
if (!isNavigationLink(item)) return null;
const { label, linkProps, additionalProps } = item;
return (
<li key={key}>
<Link {...linkProps} {...additionalProps}>{label}</Link>
</li>
);
};
const renderSection: RenderSectionFunction = (elements, key) => (
<ul key={key}>{elements}</ul>
);
export function RootLayout() {
const navigationItems = useNavigationItems();
const navigationElements = useRenderedNavigationItems(navigationItems, renderItem, renderSection);
return (
<>
<nav>{navigationElements}</nav>
<Outlet />
</>
);
}
```
### Global Data Fetching
```tsx
// Protected data
import { useProtectedDataQueries, useIsBootstrapping, AppRouter } from "@squide/firefly";
function BootstrappingRoute() {
const [session] = useProtectedDataQueries([{
queryKey: ["/api/session"],
queryFn: async () => {
const response = await fetch("/api/session");
if (!response.ok) throw new ApiError(response.status);
return response.json();
}
}], error => isApiError(error) && error.status === 401);
if (useIsBootstrapping()) return <div>Loading...</div>;
return (
<SessionContext.Provider value={session}>
<Outlet />
</SessionContext.Provider>
);
}
// In App component, set waitForProtectedData
<AppRouter waitForProtectedData>...</AppRouter>
```
```tsx
// Public data
const [data] = usePublicDataQueries([{ queryKey: [...], queryFn: ... }]);
<AppRouter waitForPublicData>...</AppRouter>
```
### Deferred Navigation Items
```tsx
export const register: ModuleRegisterFunction<FireflyRuntime, unknown, DeferredRegistrationData> = runtime => {
// Always register routes
runtime.registerRoute({ path: "/feature", element: <FeaturePage /> });
// Return function for deferred navigation items
return (deferredRuntime, { userData }) => {
if (userData.isAdmin && deferredRuntime.getFeatureFlag("enable-feature")) {
deferredRuntime.registerNavigationItem({
$id: "feature",
$label: "Feature",
to: "/feature"
});
}
};
};
```
```tsx
// Execute deferred registrations in BootstrappingRoute
const data = useMemo(() => ({ userData }), [userData]);
useDeferredRegistrations(data);
```
### MSW Request Handlers
```tsx
export const register: ModuleRegisterFunction<FireflyRuntime> = async runtime => {
if (runtime.isMswEnabled) {
const requestHandlers = (await import("../mocks/handlers.ts")).requestHandlers;
runtime.registerRequestHandlers(requestHandlers);
}
};
```
### Event Bus
```tsx
// Listen
import { useEventBusListener } from "@squide/firefly";
const handleEvent = useCallback((data, context) => { /* ... */ }, []);
useEventBusListener("event-name", handleEvent);
// Dispatch
import { useEventBusDispatcher } from "@squide/firefly";
const dispatch = useEventBusDispatcher();
dispatch("event-name", payload);
```
### Environment Variables
```tsx
// Register at initialization
const runtime = initializeFirefly({
environmentVariables: { apiBaseUrl: "https://api.example.com" }
});
// Or register in module
runtime.registerEnvironmentVariable("key", "value");
// Use
import { useEnvironmentVariable } from "@squide/firefly";
const apiUrl = useEnvironmentVariable("apiBaseUrl");
```
### Feature Flags
```tsx
// Initialize with LaunchDarkly
import { initialize as initializeLaunchDarkly } from "launchdarkly-js-client-sdk";
const ldClient = initializeLaunchDarkly("client-id", { kind: "user", anonymous: true }, { streaming: true });
await ldClient.waitForInitialization(5);
const runtime = initializeFirefly({ launchDarklyClient: ldClient });
// Use
import { useFeatureFlag } from "@squide/firefly";
const isEnabled = useFeatureFlag("feature-key", defaultValue);
```
### Logging
```tsx
import { useLogger } from "@squide/firefly";
const logger = useLogger();
logger.debug("Message");
```
### Error Boundaries
```tsx
// Root error boundary (wraps everything)
runtime.registerRoute({
errorElement: <RootErrorBoundary />,
children: [{
element: <RootLayout />,
children: [PublicRoutes, ProtectedRoutes]
}]
}, { hoist: true });
// Module error boundary (isolates module failures)
runtime.registerRoute({
element: <RootLayout />,
children: [{
errorElement: <ModuleErrorBoundary />,
children: [PublicRoutes, ProtectedRoutes]
}]
}, { hoist: true });
```
## API Quick Reference
### initializeFirefly Options
- `mode`: `"development"` | `"production"`
- `localModules`: Array of registration functions
- `context`: Object passed to registration functions
- `useMsw`: Enable MSW support
- `startMsw`: Function to start MSW
- `environmentVariables`: Initial environment variables
- `honeycombInstrumentationClient`: For tracing
- `launchDarklyClient`: For feature flags
- `loggers`: Array of logger instances
- `plugins`: Array of plugin factory functions
- `onError`: Error handler for bootstrapping errors
### Route Registration Options
- `hoist`: Register at router root (bypasses layouts/auth)
- `parentPath`: Nest under route with matching `path`
- `parentId`: Nest under route with matching `$id`
### Route Properties
- `$id`: Identifier for nesting
- `$visibility`: `"public"` | `"protected"` (default)
### Navigation Item Properties
- `$id`: Unique identifier (recommended for stable keys)
- `$label`: Text or ReactNode
- `$priority`: Sorting priority (higher = first)
- `$canRender`: Conditional render function
- `$additionalProps`: Custom props for renderer
- `to`: Route path (supports dynamic segments like `/user/:id`)
- `style`: Inline styles for the navigation item
- `target`: Link target (e.g., `"_blank"` to open in new tab)
### Navigation Registration Options
- `menuId`: Target a specific menu (default: `"root"`)
- `sectionId`: Nest under section with matching `$id`
### AppRouter Props
- `waitForPublicData`: Delay until public data ready
- `waitForProtectedData`: Delay until protected data ready
### Hooks
- `useNavigationItems(options?)`: Get navigation items
- `useRenderedNavigationItems(items, renderItem, renderSection)`: Render nav items
- `useIsBootstrapping()`: Check if bootstrapping
- `usePublicDataQueries(queries)`: Fetch public global data
- `usePublicDataHandler(handler)`: Execute handler when modules are ready
- `useProtectedDataQueries(queries, isUnauthorizedError)`: Fetch protected data
- `useProtectedDataHandler(handler)`: Execute handler when modules ready and route is protected
- `useDeferredRegistrations(data?, options?)`: Execute deferred registrations (options: `{ onError? }`)
- `useEventBusListener(event, handler, options?)`: Listen to events
- `useEventBusDispatcher()`: Get dispatch function
- `useLogger()`: Get logger instance
- `useEnvironmentVariable(key)`: Get env variable
- `useEnvironmentVariables()`: Get all env variables
- `useFeatureFlag(key, defaultValue)`: Get feature flag
- `useFeatureFlags()`: Get all feature flags
- `useLaunchDarklyClient()`: Get LaunchDarkly client instance
- `usePlugin(name)`: Get plugin instance
- `useRuntime()`: Get runtime instance
- `useRuntimeMode()`: Get runtime mode
- `useRoutes()`: Get registered routes
- `useIsRouteProtected(route)`: Check if a route is protected
- `useRouteMatch(locationArg, options?)`: Match route against location
### Helper Functions
- `isNavigationLink(item)`: Type guard for navigation links
- `isGlobalDataQueriesError(error)`: Type guard for query errors
- `resolveRouteSegments(path, params)`: Resolve dynamic segments
- `getFeatureFlag(client, key, defaultValue)`: Get flag in non-React code
- `mergeDeferredRegistrations(candidates)`: Merge multiple deferred registration functions
For detailed API documentation, see the references folder.