Skip to content
Skillv1.0.0

tswebui

Comprehensive guide for installing and using TSWebUI-shadcn components in Next.js projects. Covers ThemeProvider, ModeToggle, LocaleToggle, TsLayout, TsTopbar, TsSidebar, TsWindow, TsForm (JSON-driven

by janbkrejci(0) 0 installs
Free
Sign in to install

Free account. Installing gives you the manifest plus copy-paste snippets.

See reviews

About

Imported from janbkrejci/TSWebUI-shadcn (LLM/skills/tswebui/SKILL.md). Install upstream with npx skills add janbkrejci/TSWebUI-shadcn --skill tswebui. Copyright stays with the author.

TSWebUI-shadcn Component Library

A comprehensive UI component library built on Next.js 16 + React 19 + Shadcn/UI + Tailwind CSS v4. All components are client components ("use client").

Table of Contents


Prerequisites & Installation

Required Stack

The consuming project must have:

  • Next.js 16+ (App Router with "use client" support)
  • React 19+
  • Tailwind CSS v4 (CSS-first config)
  • Shadcn/UI initialized — if not yet set up, run:
    npx shadcn@latest init
    This creates components.json, src/lib/utils.ts (with the cn() helper using clsx + tailwind-merge), and the @/ path alias in tsconfig.json. All TSWebUI components depend on these.

How to Install a Component

All TSWebUI components are published in an online Shadcn registry. Install any component with a single command:

npx shadcn@latest add https://janbkrejci.github.io/TSWebUI-shadcn/registry/<component>.json

This command:

  1. Downloads the component source files into src/components/ts-web-ui/<component>/
  2. Installs all required npm dependencies (e.g. react-hook-form, @tanstack/react-table)
  3. Installs all required Shadcn UI primitives (e.g. input, checkbox, select)
  4. Installs all required other TSWebUI components (transitive dependencies)

No manual npm installs or file copying needed — one command does everything.

Available Components

Component Install Command
ThemeProvider npx shadcn@latest add https://janbkrejci.github.io/TSWebUI-shadcn/registry/theme-provider.json
ModeToggle npx shadcn@latest add https://janbkrejci.github.io/TSWebUI-shadcn/registry/mode-toggle.json
LocaleToggle npx shadcn@latest add https://janbkrejci.github.io/TSWebUI-shadcn/registry/locale-toggle.json
Locale (system) npx shadcn@latest add https://janbkrejci.github.io/TSWebUI-shadcn/registry/locale.json
TsLogo npx shadcn@latest add https://janbkrejci.github.io/TSWebUI-shadcn/registry/ts-logo.json
TsTopbar npx shadcn@latest add https://janbkrejci.github.io/TSWebUI-shadcn/registry/ts-topbar.json
TsSidebar npx shadcn@latest add https://janbkrejci.github.io/TSWebUI-shadcn/registry/ts-sidebar.json
TsLayout (integrated) npx shadcn@latest add https://janbkrejci.github.io/TSWebUI-shadcn/registry/integrated-layout.json
TsWindow npx shadcn@latest add https://janbkrejci.github.io/TSWebUI-shadcn/registry/ts-window.json
TsTable npx shadcn@latest add https://janbkrejci.github.io/TSWebUI-shadcn/registry/ts-table.json
TsForm npx shadcn@latest add https://janbkrejci.github.io/TSWebUI-shadcn/registry/ts-form.json

Dependency Graph

You don't need to install transitive dependencies manually. For reference, here's what each component pulls in:

  • LocaleTogglelocale, button + Shadcn dropdown-menu + npm: lucide-react
  • TsFormlocale, ts-table, button, alert-dialog + 15 Shadcn primitives (form, input, select, checkbox, radio-group, switch, slider, popover, calendar, command, dialog, separator, tabs, textarea, toggle-group) + npm: react-hook-form, lucide-react, date-fns, react-markdown, remark-gfm, react-syntax-highlighter
  • TsTablelocale, button + Shadcn checkbox, dropdown-menu, input, select, table, badge + npm: @tanstack/react-table, lucide-react, xlsx, date-fns
  • TsLayout (integrated) → ts-sidebar, ts-topbar, theme-provider (and their transitive deps)
  • TsSidebarlocale, button, ts-logo + Shadcn tooltip
  • TsTopbarts-logo
  • TsWindowlocale, button + npm: react-rnd, lucide-react
  • ModeTogglebutton + Shadcn dropdown-menu + npm: next-themes, lucide-react
  • ThemeProvider → npm: next-themes
  • TsLogo → npm: lucide-react

Where Files Are Installed

After running the install command, files appear at:

src/
├── components/
│   ├── ui/                          # Shadcn primitives (input, checkbox, etc.)
│   └── ts-web-ui/                   # TSWebUI components
│       ├── ui/                      # TSWebUI overrides (button, alert-dialog)
│       ├── ts-form/                 # TsForm + all widgets
│       │   ├── index.tsx
│       │   ├── types.ts
│       │   ├── widget-types.ts
│       │   ├── utils.ts
│       │   ├── ts-form-field.tsx
│       │   ├── ts-form-layout.tsx
│       │   ├── ts-form-confirmation-dialog.tsx
│       │   └── widgets/             # 20+ field type widgets
│       ├── ts-table/                # TsTable + sub-components
│       ├── ts-window/               # TsWindow + WindowProvider
│       ├── locale/                  # Localization (TsLocaleProvider, en, cs)
│       ├── locale-toggle/           # LocaleToggle (language switcher for TopBar)
│       ├── ts-layout/               # TsLayout (integrated shell)
│       ├── ts-sidebar/              # Sidebar system
│       ├── ts-topbar/               # TopBar
│       ├── ts-logo/                 # Logo component
│       ├── theme-provider/          # ThemeProvider
│       └── mode-toggle/             # ModeToggle
└── lib/
    └── utils.ts                     # cn() utility (created by shadcn init)

Import Convention

All imports use the @/ path alias (configured in tsconfig.json by shadcn init):

import { TsLocaleProvider, cs, en, useTsLocale, useTsLocaleSetter } from "@/components/ts-web-ui/locale"
import { LocaleToggle } from "@/components/ts-web-ui/locale-toggle"
import { ModeToggle } from "@/components/ts-web-ui/mode-toggle"
import { ThemeProvider } from "@/components/ts-web-ui/theme-provider"
import { TsForm } from "@/components/ts-web-ui/ts-form"
import { TsLayout } from "@/components/ts-web-ui/ts-layout"
import { Logo } from "@/components/ts-web-ui/ts-logo"
import { TsTable } from "@/components/ts-web-ui/ts-table"
import { TopBar, TopBarGroup } from "@/components/ts-web-ui/ts-topbar"
import {
  TsWindow,
  WindowOutlet,
  WindowProvider,
  useWindowManager,
} from "@/components/ts-web-ui/ts-window"

Quick Start Example

To add a complete application shell with forms and theme switching to a fresh Next.js project:

# 1. Initialize shadcn (if not done yet)
npx shadcn@latest init

# 2. Install the integrated layout (includes sidebar, topbar, theme provider)
npx shadcn@latest add https://janbkrejci.github.io/TSWebUI-shadcn/registry/integrated-layout.json

# 3. Install the form system (includes table and all field widgets)
npx shadcn@latest add https://janbkrejci.github.io/TSWebUI-shadcn/registry/ts-form.json

# 4. Install the theme toggle button
npx shadcn@latest add https://janbkrejci.github.io/TSWebUI-shadcn/registry/mode-toggle.json

# 5. Install the window system (if needed)
npx shadcn@latest add https://janbkrejci.github.io/TSWebUI-shadcn/registry/ts-window.json

Localization

Location: src/components/ts-web-ui/locale/

All static UI texts in every TSWebUI component are localizable. The library ships with English (default) and Czech presets and supports fully custom locale objects.

Architecture

Export Source Description
TsLocaleProvider locale/context.tsx React context provider — wrap app or subtree
useTsLocale() locale/context.tsx Hook returning the current TsLocale (accepts optional override)
useTsLocaleSetter() locale/context.tsx Hook returning { localeName, setLocaleName } — use in locale switcher components
en locale/en.ts English locale preset (default)
cs locale/cs.ts Czech locale preset
TsLocale locale/types.ts Full locale type (strings + formatting)
TsLocaleStrings locale/types.ts All translatable string keys (table, form, window, sidebar, formEditor)
TsLocaleFormatting locale/types.ts locale (BCP 47 tag, e.g. "en-US") and optional timezone (IANA, e.g. "Europe/Prague")

Import

import { TsLocaleProvider, cs, en, useTsLocale, useTsLocaleSetter } from "@/components/ts-web-ui/locale"
import type { TsLocale, TsLocaleFormatting, TsLocaleStrings } from "@/components/ts-web-ui/locale"

Usage — Context Provider

Wrap your app (or a subtree) with TsLocaleProvider. All TSWebUI components inside the provider automatically pick up the locale:

import { TsLocaleProvider } from "@/components/ts-web-ui/locale"

// Preset by name
<TsLocaleProvider locale="cs">
  {children}
</TsLocaleProvider>

// Full custom object
<TsLocaleProvider locale={myCustomLocale}>
  {children}
</TsLocaleProvider>

If no provider is present, English (en) is used.

TsLocaleProvider holds mutable state internally — the active locale can be changed at runtime by any child component using useTsLocaleSetter().

Usage — Locale Setter Hook

Use useTsLocaleSetter() in custom locale-switcher components. It returns { localeName, setLocaleName } where localeName is the active preset name string and setLocaleName changes the locale globally:

import { useTsLocaleSetter } from "@/components/ts-web-ui/locale"

function MyLocaleSwitcher() {
  const { localeName, setLocaleName } = useTsLocaleSetter()
  return (
    <button onClick={() => setLocaleName(localeName === "en" ? "cs" : "en")}>
      {localeName === "en" ? "Switch to Czech" : "Switch to English"}
    </button>
  )
}

LocaleToggle — Ready-Made Language Switcher

LocaleToggle is a pre-built dropdown for the TopBar. It uses useTsLocaleSetter() internally and shows SVG-flag buttons. Czech (🇨🇿 Česky) is listed first and is the default locale in the demo app. Install it with:

import { LocaleToggle } from "@/components/ts-web-ui/locale-toggle"
import { TopBar, TopBarGroup } from "@/components/ts-web-ui/ts-topbar"
import { ModeToggle } from "@/components/ts-web-ui/mode-toggle"

<TopBar
  rightContent={
    <TopBarGroup>
      <LocaleToggle />
      <ModeToggle />
    </TopBarGroup>
  }
/>

Usage — Component-Level Override

TsTable and TsForm accept a locale prop that overrides the context for that component and its children:

import { cs } from "@/components/ts-web-ui/locale"

<TsTable data={data} columnDefinitions={cols} locale={cs} />
<TsForm layout={layout} fields={fields} locale="cs" />

The locale prop accepts either a preset name string ("en", "cs") or a full TsLocale object.

Usage — Hook

useTsLocale(override?) returns the resolved TsLocale. It reads from context by default but accepts an optional override (string or object):

const locale = useTsLocale()         // from context
const locale = useTsLocale("cs")     // force Czech
const { strings, formatting } = locale

Creating a Custom Locale

Spread an existing preset and override only what you need:

import { en } from "@/components/ts-web-ui/locale"
import type { TsLocale } from "@/components/ts-web-ui/locale"

const myLocale: TsLocale = {
  strings: {
    ...en.strings,
    table: { ...en.strings.table, search: "Find...", noRecords: "Nothing here" },
    form: { ...en.strings.form, required: "Mandatory" },
  },
  formatting: { locale: "en-GB", timezone: "Europe/London" },
}

String Categories

TsLocaleStrings is organized into five groups:

Group Keys (selected) Used By
table search, columns, viewColumns, searchColumns, clearAllFilters, export, exportAll(count), exportFiltered(count), exportSelected(count), import, newRecord, noRecords, rowsPerPage, pageOf, rowsSelected, selectAll, copyToClipboard, moveLeft, moveRight, first/previous/next/last, importResults, etc. TsTable (toolbar, view, pagination, columns)
form required, showPassword, hidePassword, selectPlaceholder, searchPlaceholder, notFound, customValueAdd, clear, today, addFile(s), selectEntity, chooseFromList, etc. TsForm (all field widgets)
window centerOnScreen, fitToContent TsWindow (titlebar buttons)
sidebar closeMenu, openMenu, expandMenu, collapseMenu TsSidebar (aria-labels, tooltips)
nav logoText, sectionOverview, sectionComponents, sectionUtilities, sectionFormWidgets, overview, window, table, form, topbar, sidebar, integratedLayout, formEditor, themeProvider, modeToggle App layout (sidebar nav labels, logo text)
formEditor ~120 keys covering toolbar (undo/redo/reset/import/export/preview), canvas (addRow, dragFieldHere, field palette group labels, field palette type labels), drag overlay, properties panel (all field properties, button properties, every section heading, all variant options, all validation error messages). Also contains nested objects fieldTypeLabels (21 entries, one per field type) and fieldGroupLabels (6 entries for palette groups). TsFormEditor

Some keys are functions for interpolation: pageOf(page, total), rowsSelected(selected, total), selected(count), fieldNotFound(field), customValueAdd(value), selectEntity(entity), etc.


ThemeProvider

Location: src/components/ts-web-ui/theme-provider/index.tsx

Install:

npx shadcn@latest add https://janbkrejci.github.io/TSWebUI-shadcn/registry/theme-provider.json

Auto-installed dependencies: next-themes

Wraps next-themes to eliminate hydration mismatch errors. Renders a placeholder <div> until the client is mounted.

Usage

Place in your root layout.tsx:

import { ThemeProvider } from "@/components/ts-web-ui/theme-provider"

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <ThemeProvider>{children}</ThemeProvider>
      </body>
    </html>
  )
}

Behavior

  • attribute="class" — adds .dark / .light class to <html>
  • defaultTheme="system" — respects OS preference
  • enableSystem — allows system theme detection
  • disableTransitionOnChange — prevents flash on theme switch
  • Until mounted, renders <div className="min-h-screen bg-background" /> to avoid hydration errors

Theme CSS Variables

Define in globals.css:

:root {
  --background: oklch(1 0 0);
  --foreground: oklch(0.145 0 0);
  /* ... more semantic colors ... */
}

.dark {
  --background: oklch(0.145 0 0);
  --foreground: oklch(0.985 0 0);
  /* ... dark variants ... */
}

Use in components: bg-background, text-foreground, bg-primary, text-muted-foreground, etc.


ModeToggle

Location: src/components/ts-web-ui/mode-toggle/index.tsx

Install:

npx shadcn@latest add https://janbkrejci.github.io/TSWebUI-shadcn/registry/mode-toggle.json

Auto-installed dependencies: next-themes, lucide-react, TSWebUI button, Shadcn dropdown-menu

A dropdown button that switches between Light / Dark / System themes.

Usage

import { ModeToggle } from "@/components/ts-web-ui/mode-toggle"

;<ModeToggle />

Props

None. This component is self-contained. It uses useTheme() from next-themes internally.

Features

  • Sun icon in light mode, Moon icon in dark mode (animated rotation/scale)
  • Dropdown with three options: Light, Dark, System
  • Checkmark indicator on the currently active theme

TsLayout

Location: src/components/ts-web-ui/ts-layout/index.tsx

Install:

npx shadcn@latest add https://janbkrejci.github.io/TSWebUI-shadcn/registry/integrated-layout.json

Auto-installed dependencies: lucide-react, ts-sidebar, ts-topbar, theme-provider (and their transitive dependencies: ts-logo, button, Shadcn tooltip)

An integrated application shell that combines TopBar + Sidebar + main content area into a single component.

Props

Prop Type Default Description
children ReactNode Main content area
navigation NavSection[] | NavItem[] undefined Sidebar navigation data
logo ReactNode undefined Logo element for sidebar and topbar
topBarLeft ReactNode undefined Content for the left side of the topbar
topBarCenter ReactNode undefined Content for the center of the topbar
topBarRight ReactNode undefined Content for the right side of the topbar
contained boolean false If true, uses absolute positioning to fit inside a bounded container instead of full viewport

Navigation Data Types

interface NavItem {
  name: string // Unique identifier
  href: string // URL path
  label: string // Display text
  icon: LucideIcon | ReactNode // Icon component or element
  exact?: boolean // If true, match href exactly (not prefix)
}

interface NavSection {
  title: string // Section heading
  items: NavItem[] // Items in this section
}

Full Application Layout Example

"use client"

import { Home, Settings, Users } from "lucide-react"

import { TsLocaleProvider } from "@/components/ts-web-ui/locale"
import { ModeToggle } from "@/components/ts-web-ui/mode-toggle"
import { ThemeProvider } from "@/components/ts-web-ui/theme-provider"
import { TsLayout } from "@/components/ts-web-ui/ts-layout"
import { Logo } from "@/components/ts-web-ui/ts-logo"
import { TopBarGroup } from "@/components/ts-web-ui/ts-topbar"

const NAVIGATION = [
  {
    title: "Application",
    items: [
      { name: "dashboard", label: "Dashboard", href: "/", icon: Home, exact: true },
      { name: "users", label: "Users", href: "/users", icon: Users },
      { name: "settings", label: "Settings", href: "/settings", icon: Settings },
    ],
  },
]

export default function AppLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <ThemeProvider>
          <TsLocaleProvider locale="en">
            <TsLayout
              navigation={NAVIGATION}
              logo={<Logo text="My App" href="/" />}
              topBarRight={
                <TopBarGroup>
                  <ModeToggle />
                </TopBarGroup>
              }
            >
              {children}
            </TsLayout>
          </TsLocaleProvider>
        </ThemeProvider>
      </body>
    </html>
  )
}

TopBar

Location: src/components/ts-web-ui/ts-topbar/index.tsx

Install:

npx shadcn@latest add https://janbkrejci.github.io/TSWebUI-shadcn/registry/ts-topbar.json

Auto-installed dependencies: lucide-react, ts-logo

Tip: If you plan to use the full layout, install integrated-layout instead — it includes TopBar, Sidebar, and ThemeProvider.

A sticky top bar with three content slots (left, center, right). Auto-detects SidebarProvider and shows a hamburger trigger.

Props

Prop Type Default Description
leftContent ReactNode undefined Content on the left side
centerContent ReactNode undefined Content in the center (flex-1)
rightContent ReactNode undefined Content on the right side
height number 56 Height in pixels
bordered boolean true Show bottom border
showTrigger boolean true Auto-show sidebar trigger if inside SidebarProvider
className string undefined Additional CSS classes

Exported Sub-components

Component Description
TopBarGroup Flexbox wrapper (flex items-center gap-2) for grouping topbar items
Logo Re-exported from ts-logo — renders text/icon with optional link

TopBarProvider (Optional)

Provides topbar height to child components via context:

import { TopBarProvider, useTopBar } from "@/components/ts-web-ui/ts-topbar"

;<TopBarProvider height={56}>
  {/* children can call useTopBar() to get { height } */}
</TopBarProvider>

Standalone Usage

import { ModeToggle } from "@/components/ts-web-ui/mode-toggle"
import { Logo } from "@/components/ts-web-ui/ts-logo"
import { TopBar, TopBarGroup } from "@/components/ts-web-ui/ts-topbar"

;<TopBar
  leftContent={<Logo text="My App" href="/" />}
  centerContent={<Input placeholder="Search..." />}
  rightContent={
    <TopBarGroup>
      <ModeToggle />
    </TopBarGroup>
  }
  height={56}
  bordered
/>

Sidebar

Location: src/components/ts-web-ui/ts-sidebar/index.tsx

Install:

npx shadcn@latest add https://janbkrejci.github.io/TSWebUI-shadcn/registry/ts-sidebar.json

Auto-installed dependencies: lucide-react, TSWebUI button, ts-logo, Shadcn tooltip

Tip: If you plan to use the full layout, install integrated-layout instead — it includes TopBar, Sidebar, and ThemeProvider.

A fully-featured collapsible sidebar with data-driven navigation, mobile responsiveness, and localStorage persistence. All accessibility labels (close/open menu, expand/collapse) are automatically localized via useTsLocale() context.

System Components

Component Description
SidebarProvider Context provider — wraps the entire layout
Sidebar The sidebar element itself
SidebarContent Scrollable content area
SidebarHeader Header with logo and close button
SidebarSection Grouped navigation section with title
SidebarItem Single navigation item with icon
SidebarFooter Bottom section with border
SidebarInset Main content area that adjusts margins based on sidebar state
SidebarTrigger Hamburger toggle button
SidebarCollapseTrigger Circular collapse/expand button on sidebar edge

SidebarProvider Props

Prop Type Default Description
defaultOpen boolean true Initial open state
mobileBreakpoint number 768 Pixel width below which mobile behavior activates
topBarHeight number 56 Height of the topbar in pixels (for offset calculations)
width string "16rem" Sidebar width when expanded
collapsedWidth string "4rem" Sidebar width when collapsed (icons only)

Sidebar Props

Prop Type Default Description
navigation NavSection[] | NavItem[] undefined Data-driven navigation (auto-renders items)
logo ReactNode undefined Logo element for the sidebar header
className string undefined Additional CSS classes

useSidebar() Hook

Returns the sidebar context:

const {
  isOpen, // boolean — sidebar visibility
  toggle, // () => void — toggle open/closed
  open, // () => void — force open
  close, // () => void — force close
  isCollapsed, // boolean — collapsed to icon-only mode
  toggleCollapsed, // () => void — toggle collapsed state
  isMobile, // boolean — mobile viewport detected
  topBarHeight, // number — topbar height in px
  width, // string — expanded width
  collapsedWidth, // string — collapsed width
} = useSidebar()

Features

  • Responsive: Auto-closes on mobile, overlay mode with backdrop
  • Collapsible: Icon-only mode with tooltips on desktop
  • Persistent: Saves open/collapsed state to localStorage
  • Active detection: Highlights current route using usePathname()
  • Data-driven: Pass navigation prop for auto-rendered nav items with Lucide icons

TsWindow

Location: src/components/ts-web-ui/ts-window/index.tsx

Install:

npx shadcn@latest add https://janbkrejci.github.io/TSWebUI-shadcn/registry/ts-window.json

Auto-installed dependencies: react-rnd, lucide-react, TSWebUI button

A draggable, resizable window system with minimize/maximize/restore, Z-index management, and an imperative API. Titlebar button tooltips ("Center on Screen", "Fit to Content") are automatically localized via useTsLocale() context.

Architecture

Three components work together:

  1. WindowProvider — React context providing the window manager
  2. WindowOutlet — Renders all open windows (place inside a relative-positioned container)
  3. useWindowManager() — Hook to interact with the window system

TsWindowProps

Prop Type Default Description
id string | number Unique window identifier
title string "Window" Title displayed in the header bar
defaultWidth number 400 Initial width in pixels
defaultHeight number 300 Initial height in pixels
defaultTop number 100 Initial Y position in pixels
defaultLeft number 100 Initial X position in pixels
minWidth number 200 Minimum allowed width
minHeight number 100 Minimum allowed height
children ReactNode Window content

useWindowManager() Hook

Method/Property Signature Description
openWindow (content: ReactNode, options?: Partial<TsWindowProps> & { id?: string }) => void Opens a new window. If id already exists, brings it to front.
closeWindow (id: string) => void Closes the window with the given ID
getWindow (id: string) => TsWindowRef | null Returns the imperative handle for a window
windows WindowItem[] Array of currently open window objects
isInteracting boolean Whether any window is being dragged/resized
setInteracting (interacting: boolean) => void Set the interaction state

TsWindowRef (Imperative API)

Retrieved via getWindow(id) or React ref:

Method Description
minimize() Minimizes the window to a small title-only bar
maximize() Maximizes the window to fill its parent container
restore() Restores from minimized or maximized state
close() Closes the window (removes from DOM)
centerOnScreen() Centers the window within its parent container
fitToContent() Adjusts height to match content scroll height
bringToFront() Increments Z-index to place above other windows

Window Features

  • macOS-style traffic lights: Red (close), Yellow (minimize), Green (maximize/restore)
  • Double-click titlebar: Toggles maximize/restore
  • Center button: Target icon in titlebar centers window
  • Fit-to-content button: Adjusts height to content
  • Drag containment: Window header always stays within parent bounds
  • Resize containment: Window cannot be resized outside parent
  • Z-index management: Global counter ensures focused window is always on top
  • Auto-fit on mount: Window adjusts height to content on initial render
  • ResizeObserver: Adapts when parent container resizes

Complete Usage Example

"use client"

import { Button } from "@/components/ui/button"

import { WindowOutlet, WindowProvider, useWindowManager } from "@/components/ts-web-ui/ts-window"

function WindowContent({ id }: { id: string }) {
  const { getWindow } = useWindowManager()

  return (
    <div className="space-y-4">
      <p>
        Content for window <strong>{id}</strong>
      </p>
      <div className="flex gap-2">
        <Button size="sm" onClick={() => getWindow(id)?.centerOnScreen()}>
          Center
        </Button>
        <Button size="sm" variant="outline" onClick={() => getWindow(id)?.minimize()}>
          Minimize
        </Button>
        <Button size="sm" variant="destructive" onClick={() => getWindow(id)?.close()}>
          Close
        </Button>
      </div>
    </div>
  )
}

function MyApp() {
  const { openWindow } = useWindowManager()

  const handleOpen = () => {
    const id = `win-${Math.random().toString(36).substring(7)}`
    openWindow(<WindowContent id={id} />, {
      id,
      title: "My Window",
      defaultWidth: 400,
      defaultHeight: 300,
      defaultLeft: 150,
      defaultTop: 100,
    })
  }

  return (
    <div className="h-screen flex flex-col">
      <div className="p-4">
        <Button onClick={handleOpen}>Open Window</Button>
      </div>
      <div className="flex-1 relative overflow-hidden">
        <WindowOutlet />
      </div>
    </div>
  )
}

export default function Page() {
  return (
    <WindowProvider>
      <MyApp />
    </WindowProvider>
  )
}

WindowOutlet Props

Prop Type Default Description
className string undefined Additional CSS classes (default includes absolute inset-0 pointer-events-none z-50)

TsForm

Location: src/components/ts-web-ui/ts-form/index.tsx

Install:

npx shadcn@latest add https://janbkrejci.github.io/TSWebUI-shadcn/registry/ts-form.json

Auto-installed dependencies:

  • npm: react-hook-form, lucide-react, date-fns, react-markdown, remark-gfm, react-syntax-highlighter
  • TSWebUI: button, alert-dialog, ts-table
  • Shadcn: form, alert, badge, calendar, checkbox, command, dialog, input, popover, radio-group, select, separator, slider, switch, tabs, textarea, toggle-group

A fully JSON-driven form engine that generates complete forms from data definitions — including layout, validation, field types, buttons, and confirmation dialogs.

TsFormProps

Prop Type Default Description
layout TsLayout Required. Layout structure defining rows and/or tabs
fields Record<string, TsFieldDef> Required. Dictionary of field definitions keyed by field name
values Record<string, unknown> {} Initial/current form values
buttons TsButton[] [] Action buttons rendered at the bottom of the form
errors TsErrors undefined External validation errors (from server/parent)
activeTab string | number undefined Controlled active tab (label string or 0-based index)
onTabChange (tab: string | number) => void undefined Callback when user switches tabs
onAction (action: string, data: Record<string, unknown>) => void undefined Primary callback. Fires for all button actions (submit, delete, custom, etc.)
onFieldChange (name: string, value: unknown, data: Record<string, unknown>) => void undefined Fires when a field value changes. Emits on every change for all editable field types (text, number, textarea, password, date, select, checkbox, switch, etc.). Display-only types (markdown, infobox, empty, separator) never emit. De-duplication guard prevents redundant emissions when the value did not actually change.
readOnly boolean false Sets all fields to read-only and hides the button bar
className string undefined Additional CSS classes for the form element
locale string | TsLocale undefined UI locale override — preset name ("en", "cs") or full TsLocale object for all static texts. Also used for date/number formatting when TsLocale.formatting.locale is set

Layout System

TsLayout

interface TsLayout {
  tabs?: TsTab[] // Multi-tab form (takes priority over rows)
  rows?: TsRow[] // Single-page form
}

TsTab

interface TsTab {
  label: string // Tab display name
  rows: TsRow[] // Rows within this tab
}

TsRow

An array of TsRowItem:

type TsRow = TsRowItem[]

interface TsRowItem {
  field: string // Key from the fields dictionary
  width?: string // CSS grid width: "1fr", "200px", "50%", etc. (default: "1fr")
  type?: "empty" | "separator" // Special layout types (overrides field lookup)
  label?: string // Label for separator type
  align?: "left" | "center" | "right" // Horizontal alignment within grid cell
}

Layout Example

const layout: TsLayout = {
  tabs: [
    {
      label: "General",
      rows: [
        [
          { field: "firstName", width: "1fr" },
          { field: "lastName", width: "1fr" },
        ],
        [
          { field: "email", width: "2fr" },
          { field: "age", width: "100px" },
        ],
        [{ type: "separator", label: "Additional Info", field: "sep1" }],
        [{ field: "bio" }],
      ],
    },
    {
      label: "Settings",
      rows: [[{ field: "role" }, { field: "active" }]],
    },
  ],
}

Field Types Reference

All field types share these base properties:

Property Type Default Description
type TsFieldType Required. Discriminator for the field type
label string undefined Label text above the field
required boolean false Visual indicator and validation check
hidden boolean false Hidden from UI but present in data
hideLabel boolean false Hides label but preserves layout slot
disabled boolean false Disables user interaction
readonly boolean false Read-only visual state
hint string undefined Help text below the field
error string undefined Static error message (prefer errors prop for dynamic validation)
excludeFromSubmit boolean false Exclude value from submitted data
autofocus boolean false Auto-focus on mount or tab change
enterAction string undefined Action on Enter key: "submit", "focus:next", or custom action
escapeAction string undefined Action on Escape key: "clear" clears value, other strings emit action

Text / Password

{ type: "text", label: "Name", placeholder: "John", selectAllOnFocus: true }
{ type: "password", label: "Password", placeholder: "••••••" }
Property Type Description
placeholder string Placeholder text
selectAllOnFocus boolean Select all text on focus

Textarea

{ type: "textarea", label: "Bio", rows: 4, placeholder: "Tell us about yourself" }
Property Type Default Description
placeholder string Placeholder text
rows number 3 Number of visible text lines
selectAllOnFocus boolean Select all text on focus

Number

{ type: "number", label: "Age", min: 0, max: 150, step: 1, roundTo: 0, locale: "en-US" }
Property Type Description
placeholder string Placeholder text
min number Minimum value
max number Maximum value
step number Step increment
roundTo number Decimal places for rounding/display
locale string Locale for number formatting (e.g. "cs-CZ")
selectAllOnFocus boolean Select all text on focus

Slider

{ type: "slider", label: "Volume", min: 0, max: 100, step: 1 }
Property Type Description
min number Minimum value
max number Maximum value
step number Step increment

Select

{
  type: "select",
  label: "Role",
  placeholder: "Choose a role",
  options: [
    { value: "admin", label: "Administrator" },
    { value: "user", label: "User" },
  ]
}
Property Type Description
placeholder string Placeholder when no option selected
options TsFieldOptions[] | string[] Available options

Multiselect

{
  type: "multiselect",
  label: "Skills",
  placeholder: "Select skills...",
  options: ["JavaScript", "TypeScript", "React"],
  allowCustom: true,
  notFoundMessage: "No skills match."
}
Property Type Description
placeholder string Placeholder text
options TsFieldOptions[] | string[] Available options
allowCustom boolean Allow adding custom values directly from search
notFoundMessage string Message when search has no results (or custom add prompt)

Combobox

{
  type: "combobox",
  label: "Country",
  options: [{ value: "us", label: "United States" }],
  allowCustom: true,
  clearable: true,
  selectAllOnFocus: true
}
Property Type Description
placeholder string Placeholder text
options TsFieldOptions[] | string[] Available options
allowCustom boolean Allow custom values not in options
clearable boolean Show a clear button
selectAllOnFocus boolean Select all text on focus
notFoundMessage string Message when no options match

Radio

{
  type: "radio",
  label: "Gender",
  options: [
    { value: "male", label: "Male" },
    { value: "female", label: "Female" },
  ]
}
Property Type Description
options TsFieldOptions[] | string[] Available options

Checkbox

{ type: "checkbox", label: "I agree to the terms" }

Boolean value. No additional properties beyond base.

Switch

{ type: "switch", label: "Active Account" }

Boolean toggle. No additional properties beyond base.

Button Group

{
  type: "button-group",
  label: "Status",
  options: [
    { value: "draft", label: "Draft", variant: "outline" },
    { value: "published", label: "Published", variant: "default" },
  ],
  variant: "process"  // Optional: renders as chevron process stepper
}
Property Type Description
options TsFieldOptions[] | string[] Available options (each can have variant)
variant "process" Optional: renders as a horizontal process stepper with chevrons

Date

{
  type: "date",
  label: "Birth Date",
  dateFormat: "d.M.yyyy",
  locale: "cs-CZ",
  showTodayButton: true,
  showClearButton: true,
  todayButtonText: "Today",
  clearButtonText: "Clear"
}
Property Type Default Description
placeholder string Placeholder for text input
dateFormat string "d.M.yyyy" date-fns format string for display
locale string Locale for calendar (e.g. "cs-CZ")
selectAllOnFocus boolean Select text on focus
showTodayButton boolean Show "Today" button in popup
showClearButton boolean Show "Clear" button in popup
todayButtonText string Custom label for Today button
clearButtonText string Custom label for Clear button
disableFuture boolean Disallow selecting dates after today (today stays selectable)
maxDate string Latest selectable date (ISO); later dates are disabled and rejected on manual entry
minDate string Earliest selectable date (ISO); earlier dates are disabled and rejected on manual entry

Value format: The stored value for type: "date" is always a YYYY-MM-DD string (e.g. "2024-03-15"), not a JS Date object. This is timezone-safe and maps directly to a SQL DATE column. The widget accepts pre-existing Date objects or ISO strings as input but always writes back a YYYY-MM-DD string. For timestamps with time component, use type: "datetime" (which works the same way but the display format includes time). For DB best practice: store date-only values as DATE/YYYY-MM-DD, store moments as UTC ISO datetime.

Limiting the range: Use disableFuture: true for fields that can never be in the future (e.g. exchange-rate dates), or maxDate/minDate for explicit bounds. Disabled days are not clickable in the calendar and out-of-range manual text entry is rejected (reverts to the last valid value).

DateTime

Same properties as Date (including disableFuture / maxDate / minDate), but with time component. Default format: "d.M.yyyy HH:mm".

{ type: "datetime", label: "Event Start", dateFormat: "d.M.yyyy HH:mm" }

File

{
  type: "file",
  label: "Attachments",
  accept: ".pdf,.doc,image/*",
  multiple: true,
  innerLabel: "Drop files here",
  showDropZone: true,
  addFileLabel: "Add file"
}
Property Type Default Description
accept string Accepted file types (MIME or extensions)
multiple boolean Allow multiple file selection
innerLabel string Label inside the drop zone
showDropZone boolean true Show drag-and-drop area
addFileLabel string Label for the "Add file" link

File value format: Array<File | TsFileDescriptor> where:

interface TsFileDescriptor {
  id?: string | number
  name: string
  size?: number
  url?: string
  type?: string
}

Button (In-Form)

{ type: "button", label: "Generate Report", action: "generate", variant: "outline" }
// Compact icon-only button (e.g. a swap control):
{ type: "button", action: "swap", icon: "ArrowLeftRight", iconOnly: true, variant: "outline" }
Property Type Description
action string Action name emitted on click
variant TsButtonVariant Visual variant
icon string Lucide icon name rendered inside the button (e.g. ArrowLeftRight)
iconOnly boolean Render a compact, square icon-only button (no label, no full width)

Separator

{ type: "separator", label: "Section Title" }

Used via layout type: "separator" in row items. Renders a horizontal rule with optional label.

Empty

{
  type: "empty"
}

Used via layout type: "empty". Creates an invisible placeholder in the grid.

Table (Nested)

{
  type: "table",
  label: "Line Items",
  columns: [
    { key: "product", title: "Product", type: "text" },
    { key: "qty", title: "Qty", type: "number", align: "right" },
    { key: "price", title: "Price", type: "number", align: "right" },
  ],
  showCreateButton: true
}
Property Type Description
columns TsTableColumnDef[] Column definitions (same format as TsTable)
showCreateButton boolean Show "Add row" button

Value: Array<Record<string, unknown>> — array of row objects.

Relationship

{
  type: "relationship",
  label: "Assigned User",
  targetEntity: "User",
  mode: "single",            // or "multiple"
  variant: "dropdown",       // or "dialog"
  modalMaxWidth: "900px",
  displayFields: ["name", "email"],
  chipDisplayFields: ["name"],
  columns: [
    { key: "name", title: "Name", sortable: true, filterable: true },
    { key: "email", title: "Email", visible: false },
  ],
  showCreateButton: false,
  showImportButton: false,
  showExportButton: false,
  showColumnSelector: true,
  valueField: "id",
  options: [
    { id: 1, name: "John", email: "john@example.com" },
    { id: 2, name: "Jane", email: "jane@example.com" },
  ]
}
Property Type Default Description
placeholder string Placeholder text
targetEntity string Entity name for labels
mode "single" | "multiple" "single" Selection mode
variant "dropdown" | "dialog" "dropdown" UI variant (popover or modal)
modalMaxWidth string CSS max-width for the dialog variant, e.g. "900px"
displayFields string[] Fields shown in search results
chipDisplayFields string[] Fields shown in selected chip
columns TsTableColumnDef[] Full column definitions (overrides displayFields)
showCreateButton boolean false Show the nested TsTable "New record" button
showImportButton boolean false Show the nested TsTable "Import" button
showExportButton boolean false Show the nested TsTable "Export" button
showColumnSelector boolean true Show the nested TsTable column selector
enableSorting boolean true Enable sorting in the nested TsTable
enableFiltering boolean true Enable column filtering in the nested TsTable
enablePagination boolean true Enable pagination in the nested TsTable
enableRowMenu boolean true Enable row action menu in the nested TsTable
enableColumnResizing boolean true Enable column resizing in the nested TsTable
enableColumnReordering boolean true Enable column reordering in the nested TsTable
pageSize number 10 Initial nested TsTable page size
pageSizeOptions number[] Nested TsTable page size options
unhideableColumns string[] Column keys that cannot be hidden in selector
valueField string Primary key field for stored value
options Record<string, unknown>[] Available records to select from

Infobox

{
  type: "infobox",
  label: "Notice",
  content: "This form is read-only.",
  variant: "warning",  // "default" | "information" | "warning" | "success" | "destructive"
  icon: "AlertTriangle",
  closable: true
}
Property Type Description
content string Static text content
value ReactNode Dynamic content
variant TsInfoboxVariant Visual style
icon string Lucide icon name (overrides variant default)
closable boolean Allow user to dismiss

Markdown

{
  type: "markdown",
  content: "### Title\n\n**Bold** text with [links](https://example.com)"
}
Property Type Description
content string Static markdown content
value string Dynamic markdown content from form data

The markdown renderer (react-markdown + remark-gfm + react-syntax-highlighter) is lazy-loaded (code-split) and rendered inside a React.Suspense boundary. Importing <TsForm> therefore does not pull the ESM-only markdown toolchain into the module graph, so forms without markdown fields load and unit-test cleanly under next/jest (no moduleNameMapper stub required) and the heavy deps stay out of the main bundle. Tests that render a markdown field should await it (e.g. `await screen.f

*Truncated - read the full file at https://github.com/janbkrejci/TSWebUI-shadcn/blob/22c56cdb9afd650b01d22ee494eb55119f2de595/LLM/skills/tswebui/SKILL.md

Use it

Copy one of these into your project. Installing also returns the manifest and these snippets.

yaml
targets:
  - https://api.opensmartroute.ai/api/v1/registry/janbkrejci-tswebui-shadcn-tswebui/manifest   # or paste the manifest below

Manifest

An Open Capability Manifest: the router reads it to know what this does, what it costs and when to pick it.

janbkrejci-tswebui-shadcn-tswebui.ocm.jsonjson
{
  "ocm": "1",
  "id": "janbkrejci-tswebui-shadcn-tswebui",
  "kind": "skill",
  "name": "tswebui",
  "description": "Comprehensive guide for installing and using TSWebUI-shadcn components in Next.js projects. Covers ThemeProvider, ModeToggle, LocaleToggle, TsLayout, TsTopbar, TsSidebar, TsWindow, TsForm (JSON-driven forms with 20+ field types), and TsTable (advanced data grid). Use when building admin dashboards, form-heavy apps, or any UI requiring draggable windows, data tables, or dynamic forms.",
  "publisher": "janbkrejci",
  "version": "1.0.0",
  "capabilities": {
    "domains": [
      "math"
    ],
    "tags": [
      "skill-md",
      "github"
    ],
    "languages": [
      "en"
    ]
  },
  "quality_prior": 0.6,
  "examples": [
    "Comprehensive guide for installing and using TSWebUI-shadcn components in Next.js projects. Covers ThemeProvider, ModeToggle, LocaleToggle, TsLayout, TsTopbar, TsSidebar, TsWindow, TsForm (JSON-driven forms with 20+ field types), and TsTable (advanced data grid). Use when building admin dashboards, form-heavy apps, or any UI requiring draggable windows, data tables, or dynamic forms."
  ],
  "primary": false,
  "metadata": {
    "source": {
      "provider": "github",
      "repository": "https://github.com/janbkrejci/TSWebUI-shadcn",
      "path": "LLM/skills/tswebui/SKILL.md",
      "ref": "22c56cdb9afd650b01d22ee494eb55119f2de595",
      "url": "https://github.com/janbkrejci/TSWebUI-shadcn/blob/22c56cdb9afd650b01d22ee494eb55119f2de595/LLM/skills/tswebui/SKILL.md",
      "key": "janbkrejci/TSWebUI-shadcn/LLM/skills/tswebui/SKILL.md"
    }
  },
  "instructions": "# TSWebUI-shadcn Component Library\n\nA comprehensive UI component library built on **Next.js 16 + React 19 + Shadcn/UI + Tailwind CSS v4**. All components are client components (`\"use client\"`).\n\n## Table of Contents\n\n- [Prerequisites & Installation](#prerequisites--installation)\n- [Localization](#localization)\n- [ThemeProvider](#themeprovider)\n- [ModeToggle](#modetoggle)\n- [TsLayout](#tslayout)\n- [TopBar](#topbar)\n- [Sidebar](#sidebar)\n- [TsWindow](#tswindow)\n- [TsForm](#tsform)\n- [TsTable](#tstable)\n- [TsFormEditor](#tsformeditor)\n\n---\n\n## Prerequisites & Installation\n\n### Required Stack\n\nThe",
  "cost": {
    "context_tokens": 25036
  }
}

Fetch it by URL: GET /api/v1/registry/janbkrejci-tswebui-shadcn-tswebui/manifest?version=1.0.0

Reviews

Star ratings from people who tried it. One review per account; edit yours any time.

No reviews yet. Install it, try it, and be the first to rate it.