name: qcomponent description: Review a frontend UI component — props/state design, accessibility, render performance, reuse of existing components, and adherence to the project's UI conventions. Use when reviewing or building React/Dash/similar components in a change with a UI surface.
Frontend Component Review
You are a frontend component-reuse specialist for {{COMPANY_SLUG}}. The goal is to catch custom UI primitives being added when the existing component library already covers them — without forcing legitimate one-off layouts into the wrong shared component.
Scope
Default to the diff under review. {{COMPANY_SLUG}} has two frontend stacks; cover both:
git diff develop...HEAD -- \
'{{PRIMARY_REPO_NAME}}/{{CODEBASE_PATH_PREFIX}}/{{PRIMARY_REPO_NAME}}/ui/components/react/**/*.tsx' \
'{{PRIMARY_REPO_NAME}}/{{CODEBASE_PATH_PREFIX}}/{{PRIMARY_REPO_NAME}}/ui/components/react/**/*.jsx' \
'{{PRIMARY_REPO_NAME}}/{{CODEBASE_PATH_PREFIX}}/{{PRIMARY_REPO_NAME}}/ui/pages/*.py' # Dash
For each new component, JSX block, or Dash layout in the diff, decide between three outcomes — same shape as /qreuse: Reuse existing, Extract new shared, or Keep custom (justified).
Component libraries to know
Don't recommend "use a Card component" without naming the source. {{COMPANY_SLUG}} uses:
- Mantine on the React side — primary primitives.
Button,Select,MultiSelect,Modal,Group,Stack,Table,TextInput,Combobox,useForm(@mantine/form). ui/components/react/components/— {{COMPANY_SLUG}}-shared wrappers (SharedTable,EntityModal,RecordEditModal, etc.). Search this directory before suggesting a custom table or modal.- Dash +
dash-mantine-componentson the Python side —SharedTable,dmc.Select,dcc.Store(must be in global layout if any callback uses it as State — memory:feedback_dash_global_store_scope). - Hooks — when 2+ components fetch the same data, the canonical move is a shared module with module-scope cache + pure fns + hook wrappers (memory:
feedback_shared_cross_component_hooks).
If the diff introduces a JSX/Python primitive that already exists in one of these libraries, that's a finding.
Patterns to look for
For each, name the failure mode the custom code introduces (drift, missed bug fix, accessibility gap, etc.).
1. Reinventing a Mantine / dmc primitive
- Custom
<select>or<Combobox>whenMantine Select/MultiSelectworks. - Custom modal with portal logic when
EntityModal/Mantine Modalalready handles focus trap, backdrop, escape key. - Custom button that diverges from the design system's
Buttonvariants. - Custom table with manual pagination when
SharedTableexists.
2. Reinventing a shared {{COMPANY_SLUG}} wrapper
- New
RecordEditModal-shaped component whenRecordEditModalalready exists. Particularly check: does it correctly load nested resources (aliases, identifiers) separately on open? Edit modals that only consume the parent GET miss sub-resources (memory:feedback_edit_modal_load_nested). - New search/filter UI on a paginated list — must use the endpoint's
searchquery param with debounce, not client-side.filter()over the current page (memory:feedback_server_side_search_paginated_lists). - New API-fetch hook duplicating logic 2+ other components already have — extract to a shared module per
feedback_shared_cross_component_hooks.
3. {{COMPANY_SLUG}}-specific footguns
These come from your memory and recur in real PRs. Flag any of:
- Mantine
Selectdataitems with agroupkey — evengroup: undefinedtriggers group-mode crash (memory:feedback_mantine_group_property). Strip the key. - 204 response handling — every fetch path that can receive 204 must handle it (memory:
feedback_handle_204_all_components). Check thatfetchJson/ equivalent doesn't.json()an empty body. - API URL mismatch — React fetch URLs must match {{PRIMARY_REPO_NAME}} router prefixes exactly (
line-items, notline/items; memory:feedback_react_api_url_mismatch). Same for cross-repo enum casing —AInotai,PRODUCTnotproduct(feedback_cross_repo_enum_casing). - Dash
dcc.Storeplacement — must be in global layout if any callback references it as State; page-scoped Stores crash callbacks on other pages (memory:feedback_dash_global_store_scope). - Drag-drop — separate draggable from children,
stopPropagationfirst, grip handle only (memory:feedback_drag_drop_event_bubbling). - Hardcoded filters in endpoints UI hits — if UI has a toggle for
is_active, the endpoint can't hardcode it (memory:feedback_hardcoded_filters_block_toggle). Pair-review the API call site. - Cross-origin 307 — POST to a route without trailing slash gets redirected, dropping
Authorizationand silently emptying the UI (memory:feedback_cross_origin_redirect_strips_auth,feedback_fastapi_trailing_slash_redirect). Always include the trailing slash on POST. - Stale built bundle — if you're reviewing a "this UI change doesn't work" report, first suspect is the artifact, not the source (memory:
feedback_rebuild_artifacts_before_debugging).
4. Staff-only vs customer-facing
Tuning knobs / hyperparameters / debug toggles must sit behind useIs{{COMPANY_SLUG_UPPER}}Staff, not in the customer wizard (memory: feedback_staff_gate_tuning_knobs). Flag any new UI controls that look like internal-tuning that aren't gated.
5. State management issues
setStatefollowed by code that reads the new value from state — closures capture pre-setStatevalues; pass acknowledgement flags as function args, not via state reads (memory:feedback_setstate_pass_ack_explicitly).- Callbacks whose return value (success bool, response status) is ignored — memory:
feedback_check_callback_return_values.
Keep custom — when it's right
Don't push one-way toward consolidation. Custom is correct when:
- The shared component would grow conditional flags for this case (signal: 4th boolean prop, custom render slot for one caller).
- The visual / interaction pattern is genuinely one-off (a wizard's step indicator that doesn't match the design system's stepper).
- The new component is being prototyped — pre-mature extraction freezes a bad API.
When you choose Keep custom, name the reason explicitly. Silent custom code is the failure mode.
Verify before reporting
For each "use existing X" finding:
- Open the cited file:line for X and confirm it exists and matches the new code's intent — props, behavior, accessibility.
- Check the existing component's known issues — if it has a bug your new code is sidestepping, surface that, don't just recommend reuse.
- For {{COMPANY_SLUG}}-specific footguns (Mantine
group, 204, dcc.Store), confirm by reading the actual call site, not just pattern-matching the file name.
Output
Single table sorted by priority:
| # | Priority | New code (file:line) | Issue | Recommendation | Existing impl / fix |
|---|
- Priority =
P0(silent break — Mantinegroupcrash, 204 unhandled, missing trailing slash, custom drag-drop with bubbling),P1(clear duplication of a Mantine / shared wrapper),P2(style / consistency). - Recommendation =
Reuse <component> from <path>|Extract shared <name>|Keep custom (reason). - Existing impl / fix = file:line of the canonical component, or a 1–3 line snippet for the fix.
End with: Total: N findings (P0=x, P1=y, P2=z) across M components in K files.
If everything in the diff legitimately needs custom code, say so directly.
Related
/qreuse— backend / general code reuse (sister skill)./qclean— defensive cruft within components./qcheckf— function-level review (props, hooks, callbacks)./qbcheck— validate a finding before refactoring./qdebug— when the symptom is "this UI doesn't behave right", debug live before recommending refactors.