Files
gpustack-ui/CLAUDE.md
T
jialinandjialin 6509f2a4ff docs: consolidate component conventions into CLAUDE.md and skill
- Add "Dynamic add-item form fields" guidance to CLAUDE.md (pick
  component by field schema: LabelSelector / ListInput / MetadataList)
- Fold StatusTag status-display recipe into create-crud-page skill
- Remove DESIGN.md (content migrated to the above)
2026-06-29 19:44:28 +08:00

3.7 KiB

Repo

This is the open source UI (gpustack-ui). Common components, hooks, and utils are published as @gpustack/core-ui and consumed throughout src.

Always prioritize reusing common components, hooks, and utils from @gpustack/core-ui.

Task-specific conventions live in skills: use create-crud-page when building a page module, form-patterns when building cascading/dependent forms.

React State and Request Patterns

Keep data flow explicit, predictable, and performant. The triggering action is the source of truth for UI updates — not effect-driven synchronization.

1. Avoid effect-driven requests

Do not use request functions as useEffect dependencies. Trigger requests explicitly from user actions or lifecycle entry points.

// Avoid
useEffect(() => {
  fetchData();
}, [fetchData]);

2. Form requests should be action-driven

  • Fetch form data (e.g. Select options) when the form first opens.
  • If later requests depend on interactions, trigger them inside the interaction handler.
  • Do not rely on useEffect dependency changes.
// Recommended
const handleOnChange = (value) => {
  fetchData(value);
};

When one action updates multiple related states, update them all directly in the handler. Do not sync via useEffect or derive indirectly via useMemo.

const handleOnChange = (value) => {
  setState1(...);
  setState2(...);
  buildState(...);
};

If multiple states always update together, use a single state object instead of multiple useState calls — fewer rerenders, more predictable transitions.

const [state, setState] = useState({ state1: ..., state2: ..., state3: ... });

5. Prefer explicit state flow

Keep request execution, state updates, and derived calculations close to the triggering action. Avoid chaining business logic through multiple useEffect hooks.

// Prefer
const handleAction = () => {
  fetchData();
  setTableData(...);
  setSelectedRow(...);
};

6. Avoid premature memoization

Do not use useMemo / useCallback unless there is a confirmed bottleneck. Overuse adds complexity, obscures state flow, and risks stale dependencies. Optimize only when necessary.

7. Keep request logic predictable

A user interaction should clearly show: what request fires, which states update, how the UI changes. Avoid indirect update chains from dependency-driven effects.

8. Prefer action-driven architecture

Prefer action-driven updates, explicit handlers, and localized state transitions over effect-driven synchronization, cross-hook implicit updates, and reactive chains between states.

Styles

Avoid styled-components for complex or large-scale styling. Prefer:

  1. createStyles for component-scoped dynamic styles
  2. CSS Modules (xxx.module.less) for structured static styles

Common components

  • Drawer/Modal open/close: useBodyScroll from @gpustack/core-ui.
  • Status display (success/failed/processing/warning): StatusTag.
  • Permission-gated visibility: Access / useAccess.
  • Request hooks: useRequest / useQueryData / useQueryDataList from @gpustack/core-ui.
  • Table data fetching: useTableFetch from @gpustack/core-ui.

Dynamic add-item form fields

When building a form, select the add-item component from the shape of the field's data (its schema). Match the schema, don't hand-roll a list UI:

  • Plain object (key→value map) → LabelSelector.
  • String arrayListInput. Ref src/pages/llmodels/forms/backend-parameters-list.tsx.
  • Object arrayMetadataList with a custom item renderer per entry. Ref src/pages/llmodels/forms/model-lora-list.tsx.