Extensibility
Let users add their own attributes to objects at runtime without touching the developer-defined base.
standards lets your users add their own attributes to any object at runtime, without touching the developer-defined base. The schema you ship is a floor, not a ceiling.
// @noverify
import { object, text } from "@stndrds/schema";
export const contact = object({ name: "contact", label: "Contact" })
// Declared in code → system: true → protected from user modification
.attribute(text({ name: "email", label: "Email" }).required())
.attribute(text({ name: "phone", label: "Phone" }));
// Users can add their own attributes via the runtime API (see below).Protected vs custom
Anything declared in code defaults to system: true. The runtime rejects user attempts to delete or rename system attributes and objects. Attributes added at runtime through the admin UI are system: false — their creator can rename, reorder, or delete them at will.
The same default applies to the object itself. A code-declared object cannot be renamed or deleted by users, but its custom runtime attributes can still be managed freely.
Breaking in vNEXT: .system() has been removed from builders. Default is now system: true. Use .runtime() to opt out — but only in tests, seeds, and fixtures.
Adding attributes at runtime
Use the useAddAttribute hook to add a new attribute to an existing object. Pass the object's id (from the runtime registry) and an AddAttributeInput payload:
// @noverify
import { useAddAttribute } from "@stndrds/react";
const { mutate: addAttribute } = useAddAttribute();
addAttribute({
objectId: "obj-123",
attribute: {
name: "internalNotes",
type: "textarea",
label: "Internal notes",
},
});useAddAttribute returns a TanStack Query mutation. On success it invalidates the object detail, attribute list, and view caches automatically.
To remove a user-created attribute, use the useDeleteAttribute hook with { objectId, attributeId }.
Types preserved
User-added attributes are recorded in the runtime registry alongside developer-defined ones. Hooks and UI components see them identically — no extra wiring required. Their values appear in record.values keyed by attribute name, with the type inferred from the type field supplied at creation time.
Supported type values include text, textarea, number, boolean, date, status, select, and relation, among others. The full list is exported as AttributeType from @stndrds/schema.
System attributes can only be removed in code — remove the .attribute() line and rely on the boot-time schema sync to demote or delete the attribute. Users see a "protected" indicator in the UI next to any system attribute.