Customization
Since you own the component code, you can customize anything. Here are common customization patterns.
Styling
Modify Classes
Add or change Tailwind classes:
// In your copied component, modify the classes:
// Before
Customer Name
field} />
// After - with custom styling
"bg-gray-50 p-4 rounded-lg border">
"font-semibold text-blue-600 text-sm uppercase">
Customer Name
field}
className="border-blue-200 focus:border-blue-500"
/>
Theme Integration
Components use CSS variables. Update your global styles:
:root { --primary: 234 89% 74%; --primary-foreground: 0 0% 100%; --radius: 0.5rem;}Adding Fields
Extend Form Schema
import { zod } from "@spaceinvoices/js-sdk";
import { z } from "zod";
// Extend the existing schema with custom fields
const extendedCustomerSchema = zod.customers.CreateCustomerBody.extend({
// Add your custom fields
internalReference: z.string().optional(),
salesRepId: z.string().optional(),
creditLimit: z.number().min(0).optional(),
});
type ExtendedCustomer = z.infer; Add Form Field
// In your copied CustomerForm component, add the new field:
control}
name="internalReference"
render={({ field }) => (
Internal Reference
Your internal tracking number for this customer
"REF-001" {...field} />
)}
/>
control}
name="creditLimit"
render={({ field }) => (
<_FormItem>
<_FormLabel>Credit Limit
<_FormControl>
<_Input
_type="number"
_placeholder="5000"
{...field}
onChange={(_e) => field.onChange(Number(e.target.value))}
/>
<_FormMessage />
)}
/> Modifying Behavior
Change Validation
import { z } from "zod";
// Add custom validation rules
const _invoiceSchema = z
.object({
date: z.string().min(1, "Date is required"),
dueDate: z.string().min(1, "Due date is required"),
items: z
.array(
z.object({
name: z.string().min(1, "Item name required"),
quantity: z.number().min(1, "Quantity must be at least 1"),
price: z.number().min(0.01, "Price must be positive"),
}),
)
.min(1, "At least one item required"),
// Custom validation: due date must be after date
})
.refine((data) => new Date(data.dueDate) >= new Date(data.date), {
message: "Due date must be on or after invoice date",
path: ["dueDate"],
});Add Side Effects
// In your copied component, modify the mutation:
const _createInvoice = useMutation({
mutationFn: (data) => sdk.invoices.create({ entityId, body: data }),
onSuccess: (invoice) => {
// Invalidate cache
queryClient.invalidateQueries(["invoices", entityId]);
// Custom side effects
trackEvent("invoice_created", { id: invoice.id });
sendWebhook("invoice.created", invoice);
// Show notification
toast.success(`Invoice ${invoice.number} created`);
// Navigate
onSuccess?.(invoice);
},
onError: (error) => {
// Custom error handling
logError("invoice_creation_failed", error);
toast.error("Failed to create invoice");
},
});Translations
Add Language Support
// Create a translation file for your language
// components/space-invoices/customers/locales/fr.ts
export default {
"customer.name": "Nom du client",
"customer.email": "Adresse e-mail",
"customer.address": "Adresse",
"customer.city": "Ville",
"customer.country": "Pays",
"customer.taxNumber": "Numéro de TVA",
"button.save": "Enregistrer",
"button.cancel": "Annuler",
"error.required": "Ce champ est obligatoire",
};
{t("customer.name")} Using Different UI Library
If you’re not using shadcn/ui, update the imports:
// Before (shadcn/ui)import { Button } from "@/components/ui/button";import { Input } from "@/components/ui/input";
// After (your UI library)import { Button } from "@chakra-ui/react";import { Input } from "@chakra-ui/react";Best Practices
- Keep a reference copy — Store the original components for comparison
- Document changes — Comment what you modified and why
- Test after updates — When pulling updates, test your customizations
- Use composition — Wrap components instead of modifying when possible
Next Steps
- Components — Component reference
- Provider Setup — Provider configuration