Form Buttons
Build model-scoped form action buttons for SolidX form views.
Scope
Form View Buttons are form-level action components for a specific <module, model> pair.
Mandatory location:
solid-ui/src/<module-name>/admin-layout/<model-name>/extension-components/
Do not place model-scoped form button components in generic folders.
Default Implementation Flow
- Create or update a React TSX component under:
solid-ui/src/<module-name>/admin-layout/<model-name>/extension-components/
- Keep export/import style consistent with neighboring files in that model folder.
- Register the component in the owning UI module manifest:
solid-ui/src/<module-name>/<module-name>.ui-module.ts
- Keep the registration name aligned with the form layout metadata
actionvalue.
Component Props Contract
Form button action components receive extension context with shape equivalent to:
{
action: any;
params: {
moduleName: string;
modelName: string;
id?: string | number;
handlePopupClose?: () => void;
[key: string]: any;
};
formik: any;
solidFormViewMetaData: any;
}Guidance:
- Use
params.id(or equivalent context field) for record-specific actions. - Guard missing IDs for create mode or embedded contexts.
Registration
Register form action components in the owning module manifest:
import { ExtensionComponentTypes, type SolidUiModule } from "@solidxai/core-ui";
import { ApproveApplicationButton } from "./admin-layout/application/extension-components/ApproveApplicationButton";
const merchantOnboardingUiModule = {
name: "merchant-onboarding",
extensionComponents: [
{
name: "ApproveApplicationButton",
component: ApproveApplicationButton,
type: ExtensionComponentTypes.formAction,
},
],
} satisfies SolidUiModule;
export default merchantOnboardingUiModule;Layout Metadata Wiring
Buttons are triggered from form layout metadata (formButtons + attrs.action):
{
"layout": {
"type": "form",
"formButtons": [
{
"attrs": {
"label": "Approve",
"action": "ApproveApplicationButton",
"openInPopup": true,
"actionInContextMenu": false
}
}
]
}
}API Call Guidance
When a form button calls backend APIs, SolidX supports two valid integration styles.
Option A: Solid HTTP Helpers
Use @solidxai/core-ui helpers such as:
solidGetsolidPostsolidPutsolidPatchsolidDeletesolidAxios
Use this when the action is localized and you do not need shared cached API state.
Guidelines:
- Use paths like
/resourceinstead of/api/resource. - Derive IDs/context from
paramsorformik.values. - Handle loading, success, and error explicitly.
- Apply follow-up refresh behavior where required.
- Keep popup close behavior safe and deterministic on both success and error paths.
Option B: Redux / RTK Query
If your form action belongs to a larger module-owned API integration strategy, you can also place RTK Query APIs, reducers, and middleware under:
solid-ui/src/<module-name>/redux/
and register them through the same module manifest.
Use this when the button participates in shared cached state, invalidation, or module-level orchestration.
See also: Redux Module Integration and Solid HTTP API
UI/Styling Guidance (Shadcn/Solid Primitives)
For form action components:
- Prefer Solid primitives (
SolidButton,SolidMessage,SolidSpinner,SolidToast) over raw HTML controls. - Keep action areas compact (
p-3/p-4,gap-2/gap-3). - Use clear call-to-action hierarchy:
- Secondary or outlined
Cancel - Primary
ConfirmorSubmit
- Secondary or outlined
- Disable confirm action during async execution and surface loading state using the
loadingprop.
Popup Lifecycle and closePopup
When openInPopup: true is used in metadata:
- SolidX opens the popup container; your component should render clean inner content.
- Always wire cancel/close to
closePopup. - On success, either auto-close immediately or show success state with a
Closebutton.
Pattern:
import { closePopup } from "@solidxai/core-ui";
import { useDispatch } from "react-redux";
const dispatch = useDispatch();
const onClose = () => dispatch(closePopup());Example
import { useState } from "react";
import { closePopup, solidPost, SolidButton } from "@solidxai/core-ui";
import { useDispatch } from "react-redux";
export function ApproveApplicationButton({ params }: any) {
const dispatch = useDispatch();
const [loading, setLoading] = useState(false);
const onApprove = async () => {
if (!params?.id) return;
setLoading(true);
try {
await solidPost(`/application/${params.id}/approve`, {});
dispatch(closePopup());
} catch {
dispatch(closePopup());
} finally {
setLoading(false);
}
};
return (
<SolidButton loading={loading} onClick={onApprove} label="Approve" />
);
}