Troubleshooting
Solutions to common issues when building with ObjectUI.
Troubleshooting
This guide covers the most common issues you may encounter when working with ObjectUI, along with their solutions.
1. "Component type X not found"
Symptom: The SchemaRenderer renders nothing or shows a fallback, and the console logs component type "kanban-ui" not found in registry.
Cause: The plugin that provides the component type has not been imported, so it never registered itself with the ComponentRegistry.
Fix:
// Import the plugin package — registration happens on import
import '@object-ui/plugin-kanban';
import '@object-ui/plugin-grid';If you are using a custom component, register it explicitly:
import { ComponentRegistry } from '@object-ui/core';
import { MyCustomWidget } from './MyCustomWidget';
ComponentRegistry.register('my-widget', MyCustomWidget, {
namespace: 'custom',
label: 'My Widget',
category: 'plugin',
});Verify what is registered by running:
npx objectui doctor2. Build Errors with Tailwind CSS
Symptom: Tailwind utility classes are not applied. Components render without styling.
Cause: You are not importing the stylesheets the ObjectUI packages publish. Their utilities — including every themed one, such as bg-primary and border-input — are compiled at build time into each package's style.css, and nothing in your own build can reproduce the themed ones.
Fix: Import them in your main CSS file, after your own Tailwind entry, in this order:
/* src/index.css */
@import 'tailwindcss';
@import '@object-ui/components/style.css';
@import '@object-ui/fields/style.css';@object-ui/components publishes the base sheet — theme tokens, base layer, its own utilities — and every other sheet is a supplement built by subtracting everything the base already ships, so each must come after it and none styles much on its own. @object-ui/fields carries what the field widgets add. @object-ui/plugin-grid and @object-ui/plugin-kanban carry what those two plugins add; add a line for each plugin package you install:
@import '@object-ui/plugin-grid/style.css';
@import '@object-ui/plugin-kanban/style.css';If a grid or a kanban board specifically looks wrong — column headers and card surfaces flat, drag feedback and selection rings missing — that plugin's sheet is the missing import. It did not exist before either: those packages emitted no CSS at all until #4929, so on earlier versions the subpath does not resolve and upgrading is the fix, not a scanning path. The remaining @object-ui/plugin-* packages still publish no stylesheet.
If field widgets specifically look wrong — tag and badge colours flat, the rating stars not reacting to hover, the signature pad showing the wrong cursor — the fields import is the one that is missing. Note that it genuinely did not exist before: every release up to and including 17.3.0 declared the @object-ui/fields/style.css subpath while shipping no stylesheet at all (#4059), so on those versions the import fails to resolve and breaks the build. Upgrade rather than adding scanning paths.
Then check that the Tailwind 4 build plugin is actually installed and wired up — @tailwindcss/postcss in postcss.config.mjs, or @tailwindcss/vite in vite.config.ts. Without it, @import 'tailwindcss' is passed through as a plain CSS import and no utilities are generated at all.
Do not try to fix this by adding
node_modulespaths to acontentarray or an@sourceline. ObjectUI is Tailwind 4 and has notailwind.config.js; Tailwind 4 does not load one unless you opt in with@config, so on most projects those paths do nothing whatsoever. Even when they are read, scanning the published files only regenerates the shape-only utilities (inline-flex,rounded-md,h-9) the two sheets already contain — it can never produce the themed ones, because the@themeblock declaring their tokens lives in unpublished package source. Missing theme colours are always a missingstyle.cssimport, never a missing path.
3. Missing Peer Dependencies
Symptom: Build or runtime errors about missing modules like react, react-dom, or react-hook-form.
Cause: ObjectUI packages declare React 18+ as a peer dependency. Your host project must provide them.
Fix:
# Run the built-in doctor command to detect missing dependencies
npx objectui doctor
# Or install manually
pnpm add react react-dom react-hook-formThe objectui doctor command checks:
- Peer dependency versions
- Package compatibility
- Registry health
- Missing plugins referenced in schemas
4. Expression Evaluation Errors
Symptom: Expressions like ${data.user.name} render as literal strings or throw runtime errors.
Cause: Malformed expression syntax, missing context variables, or unclosed brackets.
Fix:
| Problem | Bad | Good |
|---|---|---|
Missing $ prefix | {data.name} | ${data.name} |
| Unclosed bracket | ${data.name | ${data.name} |
| Wrong context var | ${row.name} | ${data.name} |
| Nested quotes | ${"hello"} | ${'hello'} or ${data.greeting} |
Available context variables:
data.*— Current data scopeuser.*— Authenticated userparams.*— URL/query parameters
Debug expressions by enabling debug mode in SchemaRendererContext:
<SchemaRenderer schema={schema} debug={true} />This logs each expression evaluation to the browser console.
5. Schema Validation Errors
Symptom: Schema renders incorrectly or ValidationEngine throws errors about invalid schema structure.
Fix: Validate your schema before passing it to the renderer:
# CLI validation (checks against @objectstack/spec)
npx objectui validate ./path/to/schema.jsonCommon schema issues:
- Missing required
typefield on components - Invalid action type (must be one of:
script,url,api,modal,flow) - Referencing a field type that doesn't exist (e.g.,
rich-textwithout importingplugin-editor)
The validation engine lives in packages/core/src/validation/ and uses Zod schemas from @object-ui/types.
6. TypeScript Errors with Schema Types
Symptom: TypeScript cannot find types like BaseSchema, ActionSchema, or FieldWidgetProps.
Cause: @object-ui/types is not installed, or the version is mismatched.
Fix:
pnpm add @object-ui/typesThen import types from the correct entry points:
// Base types
import type { BaseSchema } from '@object-ui/types';
// Category-specific types
import type { FormSchema } from '@object-ui/types/form';
import type { LayoutSchema } from '@object-ui/types/layout';
import type { DataDisplaySchema } from '@object-ui/types/data-display';
// Zod validation schemas — same PascalCase names as the types, so alias one of
// the two when a module imports both.
import { BaseSchema as BaseSchemaValidator } from '@object-ui/types/zod';The @object-ui/types package exports multiple entry points (base, layout, form, data-display, feedback, overlay, navigation, complex, data, zod). Check packages/types/package.json for the full list.
7. Dark Mode Flickering
Symptom: Page flashes white before switching to dark mode on initial load.
Cause: The ThemeProvider (packages/react/src/context/ThemeContext.tsx) is mounted too late, or the theme preference is read asynchronously after first paint.
Fix:
- Place the
ThemeProvideras high as possible in your component tree — ideally wrapping your entire app:
import { ThemeProvider } from '@object-ui/react';
import type { FC } from 'react';
declare const YourApp: FC;
function App() {
return (
<ThemeProvider defaultTheme="system">
<YourApp />
</ThemeProvider>
);
}- Add a blocking script in your HTML
<head>to set theclassattribute before React hydrates:
<script>
const theme = localStorage.getItem('object-ui-theme') || 'system';
if (theme === 'dark' || (theme === 'system' && matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark');
}
</script>8. i18n Missing Translations
Symptom: UI shows raw translation keys like common.save instead of localized text.
Cause: Locale files are missing or the I18nProvider is not configured.
Fix:
- Ensure the
I18nProviderfrompackages/i18n/wraps your app:
import { I18nProvider } from '@object-ui/i18n';
import type { FC } from 'react';
declare const App: FC;
// The app's own packs are merged with the built-in locales.
const resources = { en: { greeting: 'Hello' }, fr: { greeting: 'Bonjour' } };
<I18nProvider config={{ defaultLanguage: 'en', resources }}>
<App />
</I18nProvider>;-
Check that locale files follow the expected structure. Each locale file should export a flat or nested object of key-value pairs.
-
Verify the locale code matches exactly (e.g.,
en,fr,de— noten-USunless your locale files use that format).
9. Performance Issues with Large Datasets
Symptom: Grid, table, or list views become slow with 1,000+ rows.
Cause: All rows are being rendered in the DOM at once.
Fix:
- Grid Plugin (
packages/plugin-grid/): Built-in grid supports virtualization out of the box for large datasets. - Paginate server-side: Configure your data source adapter (
packages/core/src/adapters/) to fetch data in pages rather than loading everything at once.
For custom views, use the usePerformance hook from @object-ui/react to monitor render times:
import { usePerformance } from '@object-ui/react';
// Inside your component:
const { metrics, markRenderStart } = usePerformance({
virtualScroll: { enabled: true, itemHeight: 40 },
});
const stopMeasure = markRenderStart(); // call stopMeasure() once the render settles
console.log(metrics.lastRenderDuration);10. Plugin Conflicts
Symptom: Two plugins register the same component type and one silently overrides the other.
Cause: Plugins registered without namespaces collide on the same type key.
Fix: Always use namespaced registrations:
import { ComponentRegistry } from '@object-ui/core';
import type { FC } from 'react';
declare const MyGridComponent: FC;
ComponentRegistry.register('grid', MyGridComponent, {
namespace: 'my-plugin', // ← prevents collision
label: 'Custom Grid',
category: 'plugin',
});The PluginSystem (packages/core/src/registry/PluginSystem.ts) uses PluginScopeImpl to auto-prefix registrations with the plugin name. If you register manually, always provide a namespace.
To debug conflicts:
npx objectui doctorThis reports duplicate registrations and namespace collisions.
10. react-day-picker v9 prop / classNames errors
Symptom: After upgrading react-day-picker (a transitive dependency of @object-ui/components) you see TypeScript errors such as:
Property 'initialFocus' does not exist on type 'DayPickerProps'Property 'fromYear' does not exist on type 'DayPickerProps''table' does not exist in type 'Partial<ClassNames>'
Cause: v9 removed several legacy props/classnames slots inherited from the v7 API.
Fix: Apply the v8 → v9 mapping below in any file that consumes <Calendar> / <DayPicker> directly:
| v8 / earlier | v9 replacement |
|---|---|
initialFocus | autoFocus |
fromYear={2000} | startMonth={new Date(2000, 0)} |
toYear={2050} | endMonth={new Date(2050, 11)} |
classNames.table | removed — drop the entry (the table wrapper now styles itself) |
The components shipped in @object-ui/components and @object-ui/plugin-calendar already use the v9 API; this note exists so downstream apps with their own <Calendar> wrappers can apply the same migration.
11. A list column is empty, or a relation column shows a raw id
Symptom: A column appears in a list/grid but every cell is blank, or a
lookup / master_detail / user column shows a record id (8UY9zHWBfjYjYor4)
instead of the related record's name. Sorting by that column does nothing, and
exports come out missing it.
Cause: The column object names its field with more than one key. The
canonical key is field — the only identity key @objectstack/spec's
ListColumnSchema declares — but stored objectui metadata also carries the
legacy name (and, in older imports, fieldName). When a column carries two
of them with different values, the renderer and the data request can resolve
two different fields: the row fetch asks the server for one field while the
grid renders another, so the cell has nothing behind it and the relation is
never expanded.
// ✗ two identities on one column
{ "field": "account", "name": "account_name" }
// ✗ legacy-only: the renderer shows it, the request used to drop it
{ "name": "account" }
// ✓ canonical
{ "field": "account" }Fix: Author columns with field. Two mechanisms keep legacy metadata
working, and both resolve the canonical key first:
- Metadata reaching a
list-viewis canonicalized at the component boundary bynormalizeListViewSchema, which stampsfieldfrom whichever spelling is present and makes any legacy key it already carries agree. - Every renderer that resolves a column — list, grid, tree, related lists, the
$expand/$selectbuilders — reads that identity through one shared function, so a surface rendered outside the fold (a standaloneobject-gridnode, for instance) still resolves the same field the request asked for.
Legacy keys are still accepted, but they are a migration bridge, not a second contract: fix the producer. A column that carries two identity keys that disagree logs a one-time dev-mode warning naming which key won and what to change — the renderer recovering is not the same as the metadata being right:
[ObjectUI] Column carries two identities: `field: 'account'` and
`name: 'account_name'`. `field` wins — it is the only key `ListColumnSchema`
declares — and `name` has been rewritten to match, so the rendered column and
the requested field agree. Fix the producer: drop `name` and author `field`
only. (objectui#3104)If you read column identity in your own code, use the one reader rather than spelling out a fallback chain — it resolves canonical-first, so it agrees with what the data layer requested:
import { columnIdentity } from '@object-ui/core';
declare const column: unknown;
const fieldName = columnIdentity(column); // string | undefinedGetting Help
If none of the above resolves your issue:
- Search existing issues
- Run
npx objectui doctorand include the output in your report - Open an issue with your schema JSON, error message, and ObjectUI package versions