SolidX
ReferenceExtending SolidXFrontend Customization

Custom Views

Clarifies how to choose between metadata-driven custom widgets and bespoke route-level pages.

Overview

In older discussions, "custom view" is used for two different patterns:

Metadata-driven custom UI blocks inside form and list layouts.

Fully bespoke route pages with independent layout and navigation.

Use the correct implementation path below.

Decision Guide

Use Custom Widget (metadata-driven form-view block)

Choose this when UI is rendered inside an existing form-view layout node with type: "custom" and should remain inside the generated Solid form flow.

  • Location: solid-ui/src/<module-name>/admin-layout/<model-name>/extension-components/
  • Registration: <module-name>.ui-module.ts via extensionComponents
  • Metadata wiring: layout JSON type: "custom", attrs.widget: "<registered-name>"

Use this path for:

  • form-view custom widgets rendered through type: "custom"
  • form buttons
  • list buttons
  • row actions
  • field widgets
  • kanban card widgets

Use Custom Page (bespoke routing)

Choose this when you need a dedicated page shell, route group, or custom navigation unrelated to metadata widgets.

  • Location: solid-ui/src/<module-name>/custom-layout/<layout-reference>/...
  • Route wiring: register route trees in <module-name>.ui-module.ts under routes.extraRoutes
  • App wiring: keep solid-ui/src/AppRoutes.tsx thin and let it consume solidUiModuleRuntime.routes
  • Do not place full custom pages under solid-ui/src/<module-name>/admin-layout/...

See Bespoke Frontend UI for the full route-level pattern.

API Convention for Both Paths

For frontend API calls, Solid supports both of these patterns:

Direct Solid HTTP helpers from @solidxai/core-ui.

Module-owned Redux and RTK Query integration under solid-ui/src/<module-name>/redux/.

Option A: Solid HTTP Helpers

Use solidGet, solidPost, solidPut, solidPatch, solidDelete, solidAxios

Guidelines:

  • Pass endpoint paths like /resource (no hardcoded /api prefix).
  • Prefer context data (params, formik.values, route params) instead of hardcoded IDs.
  • Handle loading, success, and error explicitly.
  • For filter-heavy requests, use qs.stringify(..., { encodeValuesOnly: true }) or Axios params.

Option B: Redux / RTK Query

Use this option when the custom view belongs to a module-owned API strategy and should participate in shared API state, caching, invalidation, generated hooks, or a more structured app-wide API layer.

Place RTK Query APIs under solid-ui/src/<module-name>/redux/, then register reducers and middleware in <module-name>.ui-module.ts.

See Redux Module Integration for the full module-owned pattern.

Example: Metadata Custom Widget

import { solidGet } from "@solidxai/core-ui";
import { useEffect, useState } from "react";
import type { SolidFormWidgetProps } from "@solidxai/core-ui";

export function BookSimilarTitles({ formData }: SolidFormWidgetProps) {
  const [rows, setRows] = useState<any[]>([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    const title = formData?.title;
    if (!title) return;

    const run = async () => {
      setLoading(true);
      try {
        const query = encodeURIComponent(String(title));
        const resp = await solidGet(`/books/similar?title=${query}`);
        setRows(resp?.data?.data?.records || []);
      } finally {
        setLoading(false);
      }
    };

    run();
  }, [formData?.title]);

  if (loading) return <div>Loading...</div>;
  return <div>Found: {rows.length}</div>;
}

Registration:

import { ExtensionComponentTypes, type SolidUiModule } from "@solidxai/core-ui";
import { BookSimilarTitles } from "./admin-layout/book/extension-components/BookSimilarTitles";

const libraryUiModule = {
  name: "library",
  extensionComponents: [
    {
      name: "BookSimilarTitles",
      component: BookSimilarTitles,
      type: ExtensionComponentTypes.formWidget,
    },
  ],
} satisfies SolidUiModule;

export default libraryUiModule;

Layout usage:

{
  "type": "custom",
  "attrs": {
    "name": "book-similar-widget",
    "widget": "BookSimilarTitles"
  }
}

See Also