// Standard path pattern
{apps,libs}/*/lib/[model]/[Model].Template.tsx
// Examples:
libs/game/lib/map/Map.Template.tsx
apps/lu/lib/feeling/Feeling.Template.tsx
// Single-file pattern
export const General = () => { /* main form */ }
export const Advanced = () => { /* advanced fields */ }
// Multi-file pattern (recommended)
[Model].Template/
├─ General.tsx
├─ Sections/
│ ├─ BasicInfo.tsx
│ ├─ ContactDetails.tsx
useEffect(() => {
if (id) {
// Load existing data
void st.do.load[Model](id);
}
return () => {
// Reset form on unmount
st.do.reset[Model]Form();
};
}, [id]);
field | Description | Example |
---|---|---|
Text | Standard text input field |
|
TextArea | Multi-line text input |
|
Select | Dropdown selection input |
|
ToggleSelect | Toggleable options selector |
|
Img | Image upload component |
|
Parent | Relationship selector |
|
Standard text input field
<Field.Text />
Multi-line text input
<Field.TextArea />
Dropdown selection input
<Field.Select />
Toggleable options selector
<Field.ToggleSelect />
Image upload component
<Field.Img sliceName='model' />
Relationship selector
<Field.Parent sliceName='relatedModel' />
<Field.List
label={l.field("map", "spawnPositions")}
value={form.spawnPositions}
onAdd={() => st.do.addSpawnPosition(defaultPosition)}
renderItem={(position, idx) => (
<div className="flex gap-4">
<Field.Text
value={position.key}
onChange={(v) => st.do.writeOnMap(["spawnPositions", idx, "key"], v)}
/>
<Field.DoubleNumber
value={position.position}
onChange={(v) => st.do.writeOnMap(["spawnPositions", idx, "position"], v)}
/>
</div>
)}
/>
<Field.Email
value={form.email}
validate={(v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v)}
errorMessage="Invalid email format"
required
/>
Option | Type | Default | Description | Example |
---|---|---|---|---|
required | boolean | false | Makes field mandatory |
|
min/max | number | - | Number range constraints |
|
validate | function | - | Custom validation function |
|
errorMessage | string | - | Custom error message |
|
Makes field mandatory
required
Number range constraints
min={0} max={100}
Custom validation function
validate={(v) => isValidEmail(v)}
Custom error message
errorMessage="Invalid format"
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
<Field.Text label="First Name" />
<Field.Text label="Last Name" />
<Field.Email label="Email" className="md:col-span-2" />
</div>
{form.type === "business" && (
<section className="mt-6 p-4 bg-base-200 rounded-lg">
<h3 className="text-lg font-semibold mb-3">Business Details</h3>
<Field.Text label="Company Name" />
<Field.Text label="Tax ID" />
</section>
)}
// Simple field update
st.do.setNameOn[Model](value);
// Nested field update
st.do.writeOn[Model](["address", "city"], value);
// List operations
st.do.addItemOn[Model](newItem);
st.do.removeItemOn[Model](index);
const FormSection = React.memo(({ fields }) => {
// Component implementation
});
// Debounced inputs
import { useDebounce } from "@akanjs/hooks";
const [input, setInput] = useState("");
const debouncedInput = useDebounce(input, 300);
// Proper labeling
<label htmlFor="email-field">Email</label>
<input id="email-field" type="email" />
// Error messaging
<div role="alert" aria-live="polite">
{error && <p className="text-error">{error}</p>}
</div>
// AddressSection.tsx
export const AddressSection = () => (
<div className="space-y-3">
<Field.Text label="Street" />
<Field.Text label="City" />
<Field.Text label="Zip Code" />
</div>
);
const handleSubmit = async () => {
try {
// Validate form
const errors = validateForm(form);
if (Object.keys(errors).length) throw errors;
// Submit data
if (id) await st.do.updateModel(id, form);
else await st.do.createModel(form);
// Redirect on success
router.push("/success");
} catch (error) {
// Handle API errors
st.do.setFormErrors(error);
}
};
issue | Description | Example |
---|---|---|
Form Not Updating | Verify Zustand store actions are properly connected |
|
Type Mismatches | Ensure constant types match form state |
|
Missing Translations | Verify dictionary keys and scopes |
|
Verify Zustand store actions are properly connected
st.do.setNameOn[Model]
Ensure constant types match form state
ProjectInput vs ProjectForm
Verify dictionary keys and scopes
l.field("model", "name")