Just started a new project and decided to try this one out. I had fun with react-admin in the past.
Brand new project and biome immediately isn't happy. Many things can be fixed automatically by biome, some are more structural.
For example:
- lots of explicit any
- breadcrumbs use index as key in map (
<React.Fragment key={index}>)
- autocomplete-array-input.tsx button has no (required) type prop
- autocomplete-input.tsx useCallback hook specifies more dependencies than necessary (also useCallback is not needed anymore with react compiler I think)
There are more, but these are the ones I saw first so I used them as examples.
I am attempting to fix some, and I can list my findings in here. Some of these are legitimate issues.
EDIT: I've asked my agent to make a summary of the real issues. This excludes fixes I already made (the examples I mentioned above, except for the key one. That one is a structural issue I am unsure how to fix)
AI summary
shadcn-admin-kit / shadcn/ui Lint Issues
Biome lint diagnostics found in apps/dashboard/src/components/admin/ and apps/dashboard/src/components/ui/. These are third-party files from shadcn-admin-kit and shadcn/ui. The noExplicitAny warnings have been excluded since they are pervasive and expected in ra-core generic patterns.
71 diagnostics across 20 rules in 25 files.
Accessibility (a11y)
useFocusableInteractive -- interactive role without focusability
| File |
Line |
Description |
components/admin/field-toggle.tsx |
119 |
<li> with role="option" is not focusable; needs tabIndex |
components/admin/filter-form.tsx |
476 |
<div> with role="menuitemcheckbox" is not focusable; needs tabIndex |
components/admin/select-input.tsx |
280 |
<div> with role="button" is not focusable; needs tabIndex |
components/ui/breadcrumb.tsx |
54 |
<span> with role="link" is not focusable; needs tabIndex |
useSemanticElements -- ARIA role should use semantic HTML instead
| File |
Line |
Description |
components/admin/form.tsx |
68 |
role="group" should use <fieldset> |
components/admin/select-input.tsx |
281 |
role="button" should use <button> |
components/ui/breadcrumb.tsx |
56 |
role="link" should use <a> or <link> |
components/ui/pagination.tsx |
13 |
role="navigation" on <nav> -- redundant, use the semantic element directly |
useKeyWithClickEvents -- onClick without keyboard handler
| File |
Line |
Description |
components/admin/filter-form.tsx |
476 |
<div> has onClick but no onKeyDown/onKeyUp |
components/admin/select-input.tsx |
280 |
<div> has onClick but no onKeyDown/onKeyUp |
useButtonType -- <button> missing explicit type
| File |
Line |
Description |
components/admin/columns-button.tsx |
191 |
<button> missing type prop |
components/admin/text-array-input.tsx |
134 |
<button> missing type prop |
noNoninteractiveElementToInteractiveRole
| File |
Line |
Description |
components/admin/field-toggle.tsx |
121 |
<li> given role="option"; should use <div> or <span> |
noSvgWithoutTitle
| File |
Line |
Description |
components/admin/login-page.tsx |
58 |
<svg> has no <title> element |
noRedundantRoles
| File |
Line |
Description |
components/ui/pagination.tsx |
13 |
role="navigation" on <nav> is redundant |
Suspicious / Correctness
noGlobalIsNan -- use Number.isNaN instead of global isNaN
| File |
Line |
Description |
components/admin/date-input.tsx |
151 |
isNaN(new Date(target.valueAsDate).getTime()) |
components/admin/date-input.tsx |
184 |
isNaN(new Date(localInputRef.current.valueAsDate).getTime()) |
components/admin/date-input.tsx |
257 |
isNaN(value.getDate()) |
components/admin/date-time-input.tsx |
121 |
isNaN(new Date(target.value).getTime()) |
components/admin/date-time-input.tsx |
152 |
isNaN(new Date(localInputRef.current.value).getTime()) |
components/admin/date-time-input.tsx |
221 |
isNaN(value.getDate()) |
components/admin/number-field.tsx |
78 |
isNaN() in number formatting |
components/admin/number-input.tsx |
128 |
isNaN() in number parsing |
noDoubleEquals -- == instead of ===
| File |
Line |
Description |
components/admin/columns-button.tsx |
273 |
index == Number(index1) |
components/admin/columns-button.tsx |
274 |
index == Number(index2) |
noImplicitAnyLet -- let without type or initializer
| File |
Line |
Description |
components/admin/columns-button.tsx |
278 |
let newColumnRanks |
components/admin/form.tsx |
200 |
let errors |
noConfusingVoidType -- void in union type
| File |
Line |
Description |
components/admin/date-time-input.tsx |
293 |
void | (() => void) should be undefined | (() => void) |
useIterableCallbackReturn -- forEach callback should not return
| File |
Line |
Description |
components/admin/date-time-input.tsx |
308 |
forEach callback implicitly returns from refCleanup?.() |
noShadowRestrictedNames -- shadowing global Error
| File |
Line |
Description |
components/admin/error.tsx |
21 |
Export named Error shadows global Error |
components/admin/layout.tsx |
6 |
Import named Error shadows global Error |
noDocumentCookie -- direct document.cookie assignment
| File |
Line |
Description |
components/ui/sidebar.tsx |
83 |
Direct document.cookie assignment; consider Cookie Store API |
noArrayIndexKey -- array index used as React key
| File |
Line |
Description |
components/admin/breadcrumb.tsx |
115 |
Index key in React.Fragment |
components/admin/columns-button.tsx |
203 |
Index key in DataTableColumnRankContext.Provider |
components/admin/columns-button.tsx |
206 |
Index key in DataTableColumnFilterContext.Provider |
components/admin/file-field.tsx |
77 |
Index key in <li> |
components/admin/file-input.tsx |
267 |
Index key in FileInputPreview |
components/admin/filter-form.tsx |
371 |
Index key in DropdownMenuItem |
components/admin/filter-form.tsx |
396 |
Index key in DropdownMenuItem |
components/admin/image-field.tsx |
68 |
Index key in <li> |
components/admin/single-field-list.tsx |
56 |
Index key in RecordContextProvider |
components/admin/text-array-input.tsx |
132 |
Index key in Badge |
Correctness / Dependencies
useExhaustiveDependencies -- unnecessary or malformed hook dependencies
| File |
Line(s) |
Description |
components/admin/date-input.tsx |
108, 128 |
useEffect lists unnecessary dep setInputKey |
components/admin/date-time-input.tsx |
82, 102 |
useEffect lists unnecessary dep setInputKey |
components/admin/date-time-input.tsx |
311 |
Dependencies list is not an array literal (variable refs passed) |
components/admin/date-time-input.tsx |
330 |
Dependencies list is not an array literal (variable refs passed) |
components/admin/edit-guesser.tsx |
59, 61 |
useEffect lists unnecessary dep resource |
components/admin/filter-form.tsx |
274, 289 |
useCallback lists unnecessary dep setOpen |
components/admin/filter-form.tsx |
292, 297 |
useCallback lists unnecessary dep setOpen |
components/admin/list-guesser.tsx |
98, 100 |
useEffect lists unnecessary dep resource |
components/admin/show-guesser.tsx |
52, 54 |
useEffect lists unnecessary dep resource |
components/ui/sidebar.tsx |
89, 91 |
useCallback lists unnecessary dep setOpenMobile |
components/ui/sidebar.tsx |
113, 123 |
useMemo lists unnecessary dep setOpenMobile |
Style / Complexity
noNonNullAssertion -- ! postfix assertion
| File |
Line |
Description |
components/admin/columns-button.tsx |
258 |
source! |
components/admin/columns-button.tsx |
300 |
source! |
components/admin/columns-button.tsx |
307 |
source! in filter callback |
components/admin/columns-button.tsx |
309 |
source! in spread |
components/admin/data-table.tsx |
356 |
source! in includes() |
components/admin/data-table.tsx |
447 |
source! in includes() |
components/admin/data-table.tsx |
469 |
source! in get() call |
components/admin/field-toggle.tsx |
101 |
selectedItem.dataset.index! |
components/admin/field-toggle.tsx |
101 |
dropIndex.current! |
components/admin/filter-form.tsx |
104 |
event.currentTarget.dataset.key! |
useOptionalChain -- prefer ?. over &&
| File |
Line(s) |
Description |
components/admin/export-button.tsx |
69-75 |
exporter && exporter(...) -> exporter?.(...) |
components/admin/login-page.tsx |
43 |
error && error.message -> error?.message |
components/admin/reference-many-field.tsx |
116 |
render && render(listContext) -> render?.(listContext) |
components/admin/simple-form-iterator.tsx |
211 |
disableRemove && disableRemove(record) -> disableRemove?.(record) |
components/admin/simple-form-iterator.tsx |
107 |
records && records[index] -> records?.[index] |
noUselessFragments -- unnecessary <>...</>
| File |
Line |
Description |
components/admin/reference-field.tsx |
53 |
Fragment wrapping single child |
components/admin/reference-field.tsx |
116 |
Fragment wrapping single child |
Performance
noAccumulatingSpread -- O(n^2) spread in reduce
| File |
Line |
Description |
components/admin/toggle-filter-button.tsx |
68 |
Spread in .reduce() accumulator |
Just started a new project and decided to try this one out. I had fun with react-admin in the past.
Brand new project and biome immediately isn't happy. Many things can be fixed automatically by biome, some are more structural.
For example:
<React.Fragment key={index}>)There are more, but these are the ones I saw first so I used them as examples.
I am attempting to fix some, and I can list my findings in here. Some of these are legitimate issues.
EDIT: I've asked my agent to make a summary of the real issues. This excludes fixes I already made (the examples I mentioned above, except for the key one. That one is a structural issue I am unsure how to fix)
AI summary
shadcn-admin-kit / shadcn/ui Lint Issues
Biome lint diagnostics found in
apps/dashboard/src/components/admin/andapps/dashboard/src/components/ui/. These are third-party files from shadcn-admin-kit and shadcn/ui. ThenoExplicitAnywarnings have been excluded since they are pervasive and expected in ra-core generic patterns.71 diagnostics across 20 rules in 25 files.
Accessibility (a11y)
useFocusableInteractive-- interactive role without focusabilitycomponents/admin/field-toggle.tsx<li>withrole="option"is not focusable; needstabIndexcomponents/admin/filter-form.tsx<div>withrole="menuitemcheckbox"is not focusable; needstabIndexcomponents/admin/select-input.tsx<div>withrole="button"is not focusable; needstabIndexcomponents/ui/breadcrumb.tsx<span>withrole="link"is not focusable; needstabIndexuseSemanticElements-- ARIA role should use semantic HTML insteadcomponents/admin/form.tsxrole="group"should use<fieldset>components/admin/select-input.tsxrole="button"should use<button>components/ui/breadcrumb.tsxrole="link"should use<a>or<link>components/ui/pagination.tsxrole="navigation"on<nav>-- redundant, use the semantic element directlyuseKeyWithClickEvents-- onClick without keyboard handlercomponents/admin/filter-form.tsx<div>hasonClickbut noonKeyDown/onKeyUpcomponents/admin/select-input.tsx<div>hasonClickbut noonKeyDown/onKeyUpuseButtonType--<button>missing explicittypecomponents/admin/columns-button.tsx<button>missingtypepropcomponents/admin/text-array-input.tsx<button>missingtypepropnoNoninteractiveElementToInteractiveRolecomponents/admin/field-toggle.tsx<li>givenrole="option"; should use<div>or<span>noSvgWithoutTitlecomponents/admin/login-page.tsx<svg>has no<title>elementnoRedundantRolescomponents/ui/pagination.tsxrole="navigation"on<nav>is redundantSuspicious / Correctness
noGlobalIsNan-- useNumber.isNaNinstead of globalisNaNcomponents/admin/date-input.tsxisNaN(new Date(target.valueAsDate).getTime())components/admin/date-input.tsxisNaN(new Date(localInputRef.current.valueAsDate).getTime())components/admin/date-input.tsxisNaN(value.getDate())components/admin/date-time-input.tsxisNaN(new Date(target.value).getTime())components/admin/date-time-input.tsxisNaN(new Date(localInputRef.current.value).getTime())components/admin/date-time-input.tsxisNaN(value.getDate())components/admin/number-field.tsxisNaN()in number formattingcomponents/admin/number-input.tsxisNaN()in number parsingnoDoubleEquals--==instead of===components/admin/columns-button.tsxindex == Number(index1)components/admin/columns-button.tsxindex == Number(index2)noImplicitAnyLet--letwithout type or initializercomponents/admin/columns-button.tsxlet newColumnRankscomponents/admin/form.tsxlet errorsnoConfusingVoidType--voidin union typecomponents/admin/date-time-input.tsxvoid | (() => void)should beundefined | (() => void)useIterableCallbackReturn--forEachcallback should not returncomponents/admin/date-time-input.tsxforEachcallback implicitly returns fromrefCleanup?.()noShadowRestrictedNames-- shadowing globalErrorcomponents/admin/error.tsxErrorshadows globalErrorcomponents/admin/layout.tsxErrorshadows globalErrornoDocumentCookie-- directdocument.cookieassignmentcomponents/ui/sidebar.tsxdocument.cookieassignment; consider Cookie Store APInoArrayIndexKey-- array index used as React keycomponents/admin/breadcrumb.tsxReact.Fragmentcomponents/admin/columns-button.tsxDataTableColumnRankContext.Providercomponents/admin/columns-button.tsxDataTableColumnFilterContext.Providercomponents/admin/file-field.tsx<li>components/admin/file-input.tsxFileInputPreviewcomponents/admin/filter-form.tsxDropdownMenuItemcomponents/admin/filter-form.tsxDropdownMenuItemcomponents/admin/image-field.tsx<li>components/admin/single-field-list.tsxRecordContextProvidercomponents/admin/text-array-input.tsxBadgeCorrectness / Dependencies
useExhaustiveDependencies-- unnecessary or malformed hook dependenciescomponents/admin/date-input.tsxuseEffectlists unnecessary depsetInputKeycomponents/admin/date-time-input.tsxuseEffectlists unnecessary depsetInputKeycomponents/admin/date-time-input.tsxrefspassed)components/admin/date-time-input.tsxrefspassed)components/admin/edit-guesser.tsxuseEffectlists unnecessary depresourcecomponents/admin/filter-form.tsxuseCallbacklists unnecessary depsetOpencomponents/admin/filter-form.tsxuseCallbacklists unnecessary depsetOpencomponents/admin/list-guesser.tsxuseEffectlists unnecessary depresourcecomponents/admin/show-guesser.tsxuseEffectlists unnecessary depresourcecomponents/ui/sidebar.tsxuseCallbacklists unnecessary depsetOpenMobilecomponents/ui/sidebar.tsxuseMemolists unnecessary depsetOpenMobileStyle / Complexity
noNonNullAssertion--!postfix assertioncomponents/admin/columns-button.tsxsource!components/admin/columns-button.tsxsource!components/admin/columns-button.tsxsource!in filter callbackcomponents/admin/columns-button.tsxsource!in spreadcomponents/admin/data-table.tsxsource!inincludes()components/admin/data-table.tsxsource!inincludes()components/admin/data-table.tsxsource!inget()callcomponents/admin/field-toggle.tsxselectedItem.dataset.index!components/admin/field-toggle.tsxdropIndex.current!components/admin/filter-form.tsxevent.currentTarget.dataset.key!useOptionalChain-- prefer?.over&&components/admin/export-button.tsxexporter && exporter(...)->exporter?.(...)components/admin/login-page.tsxerror && error.message->error?.messagecomponents/admin/reference-many-field.tsxrender && render(listContext)->render?.(listContext)components/admin/simple-form-iterator.tsxdisableRemove && disableRemove(record)->disableRemove?.(record)components/admin/simple-form-iterator.tsxrecords && records[index]->records?.[index]noUselessFragments-- unnecessary<>...</>components/admin/reference-field.tsxcomponents/admin/reference-field.tsxPerformance
noAccumulatingSpread-- O(n^2) spread in reducecomponents/admin/toggle-filter-button.tsx.reduce()accumulator