Plugins

Stately plugins provide additional capabilities for your application. Each plugin includes coordinated backend (Rust) and frontend (TypeScript) packages.

Available Plugins

Files Plugin

File upload, versioning, and management capabilities.

PackageDescription
stately-filesRust crate with upload endpoints and path types
@statelyjs/filesTypeScript package with file browser and form fields

Key Features:

  • Multipart and JSON file uploads
  • Automatic UUID v7 versioning
  • Directory listing and navigation
  • RelativePath field type for entity forms
  • File manager page component

Files Plugin Documentation

Arrow Plugin

Data connectivity and SQL query execution via Apache Arrow.

PackageDescription
stately-arrowRust crate with connector registry and query engine
@statelyjs/arrowTypeScript package with data explorer and query components

Key Features:

  • Multiple backend connectors (S3, GCS, Azure, ClickHouse)
  • SQL query execution with DataFusion
  • Streaming Arrow IPC responses
  • Catalog and schema discovery
  • High-performance data table component
  • Plans to build out the most performant large-data arrow viewer available

Arrow Plugin Documentation

Installing Plugins

Backend

Add the plugin crate to your Cargo.toml. Verify versions as docs can become outdated:

cargo add stately-files stately-arrow

Frontend

Install the npm package:

npm
yarn
pnpm
bun
deno
npm install @statelyjs/files @statelyjs/arrow

Integrating Plugins

Backend Integration

  1. Implement state extraction for the plugin
  2. Add the plugin router to your application
use stately_files::{router as files_router, FileState, Dirs};

use crate::state::ApiState;

// State extraction
impl FromRef<ApiState> for FileState {
    fn from_ref(state: &ApiState) -> Self {
        FileState::new(Dirs::new(cache_dir, data_dir))
    }
}

// Router integration
pub fn app(state: ApiState) -> Router {
    Router::new()
        .nest("/api/files", files_router(state.clone()))
        .with_state(state)
}

Frontend Integration

  1. Add schema and UI plugins to your runtime
  2. Configure the API path prefix
import { statelyUi, statelyUiProvider, useStatelyUi } from '@statelyjs/stately';
import { type DefineConfig, type Schemas, stately } from '@statelyjs/stately/schema';
import { type FilesPlugin, type FilesUiPlugin, filesPlugin, filesUiPlugin } from '@statelyjs/files';

import openApiSpec from '../../openapi.json';
import { PARSED_SCHEMAS, type ParsedSchema } from '../generated/schemas';
import type { components, operations, paths } from '../generated/types';

// Define app schema with plugin extensions 
type AppSchemas = Schemas<
  DefineConfig<components, paths, operations, ParsedSchema>,
  readonly [FilesPlugin]
>;

const schema = stately<AppSchemas>(openApiSpec, PARSED_SCHEMAS)
  .withPlugin(filesPlugin());

const runtime = statelyUi<AppSchemas, readonly [FilesUiPlugin]>({ client, schema, core, options })
  .withPlugin(filesUiPlugin({ api: { pathPrefix: '/api/files' } }));

Plugin Architecture

Plugins follow a consistent architecture:

Backend:

  • Router factory function generic over application state
  • State definition and state extraction via Axum's FromRef trait
  • OpenAPI generation for type codegen
  • Request, Response, and Parameter types

Frontend:

  • Schema plugin and types for type system extensions
  • UI plugin and types for component registration, route navigation, and utilities
  • Typed API client and hooks
  • Pre-built components, views, and pages

See Plugin Development for details on creating your own plugins.