templates/basic/.claude/skills/dashboard-tab-ui/SKILL.md
Create or modify tabbed workflows in admin or vendor dashboards in the Mercur basic starter using stable tab structure, validation scope, and i18n-aware wizard conventions.
npx skillsauth add mercurjs/mercur dashboard-tab-uiInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Use this skill when:
Applies equally to admin and vendor dashboards.
Before introducing custom interactive UI inside a tab, apply medusa-ui-conformance.
Always use TabbedForm from @mercurjs/dashboard-shared — do NOT build custom tab navigation with ProgressTabs from @medusajs/ui directly.
TabbedForm handles internally: RouteFocusModal.Form, ProgressTabs in the header navbar, KeyboundForm, and a default footer with Cancel/Continue/Save.
form — UseFormReturn from react-hook-formonSubmit — submit handler from form.handleSubmit()isLoading (optional) — disables submit buttonfooter (optional) — custom footer render prop; omit to use the defaultid (string, required) — unique tab identifierlabel (string, required) — display label in the tab barvalidationFields (FieldPath[], optional) — fields to validate on this tabisVisible ((form) => boolean, optional) — dynamic tab visibilityThe create page wraps RouteFocusModal around the form component:
const CreatePage = () => (
<RouteFocusModal>
<CreateForm />
</RouteFocusModal>
);
export default CreatePage;
The form component renders TabbedForm directly — do NOT nest it inside another RouteFocusModal.Form:
const CreateForm = () => {
const { handleSuccess } = useRouteModal();
const form = useForm({ resolver: zodResolver(schema), defaultValues: { ... } });
const handleSubmit = form.handleSubmit(async (data) => {
handleSuccess("/entity-route");
});
return (
<TabbedForm form={form} onSubmit={handleSubmit}>
<TabbedForm.Tab id="general" label="General">
<div className="flex flex-col gap-y-4 p-8">
{/* Form.Field components */}
</div>
</TabbedForm.Tab>
</TabbedForm>
);
};
TabbedForm's internal onNext calls form.trigger() without arguments, which validates the entire form — not just the active tab's fields. Required fields on later tabs will prevent Continue from working.
Workaround: Only mark fields as required (.min(1)) if they are on the first tab. Use .optional() for fields on later tabs and validate at submit time if needed.
ProgressTabs directly — use TabbedForm.TabbedForm in RouteFocusModal.Form — it does this internally.footer unless you need to change button labels — the default footer handles Cancel/Continue/Save correctly.Heading level="h2") and grouped sections inside each tab body.className="p-8" to the wrapping div inside each TabbedForm.Tab for consistent spacing.Form.Field / Form.Item / Form.Label / Form.Control / Form.ErrorMessage from @mercurjs/dashboard-shared.zodResolver with react-hook-form for schema validation.dashboard-page-uidashboard-form-uidocumentation
Analyze a Mercur 1.x project and guide migration to 2.0. Self-contained — works without access to the mercur monorepo.
documentation
Plan and execute migration from Mercur 1.x to 2.0. Classifies project difficulty, reads relevant migration docs, and follows stop conditions.
development
Review code changes for contract compliance, type safety, and regression risk. Use after completing any non-trivial implementation, before merging PRs, or when asked to review code quality across any mercur package.
tools
Use Mercur CLI commands correctly inside a project created from the Mercur basic starter. Use when choosing between `create`, `init`, `add`, `search`, `view`, and `diff`.