React
Come costruire una pagina impostazioni che regge la crescita
Una pagina impostazioni regge la crescita se le sezioni sono dati, non JSX copiato. Ecco struttura, salvataggio per sezione e il limite dei blocchi pronti.
In sintesi
- Una sezione, una route e un salvataggio: non dodici tab in una pagina sola.
- Le impostazioni sono dati: aggiungere un'opzione deve costare un oggetto.
- I blocchi del catalogo dichiarano «see source»: verifica la licenza prima di spedire.
Una pagina impostazioni che regge la crescita è un elenco di sezioni dichiarate come dati, non una sequenza di componenti scritti a mano. Aggiungere un'opzione deve costare un oggetto, non una copia di JSX.
Il punto in cui si rompe è prevedibile: la terza sezione. Arriva il primo salvataggio per sezione, poi il primo campo che dipende da un altro, poi il primo permesso negato.
Una sezione, un salvataggio
- Una route per sezione: /settings/notifications deve esistere come link.
- Stato locale al form e salvataggio esplicito, non un autosave invisibile.
- Conferma ed errore vicini al pulsante che li ha prodotti.
- Campi disabilitati spiegati, non solo spenti.
- Permessi letti dal server: il client non decide chi può cambiare cosa.
Cosa c'è nel catalogo
Il blocco settings di 1st-pouf mostra sezioni a tab con react-hook-form; openlmis-settings-list è la variante a righe con badge e valori in sola lettura. Sfoglia la categoria settings. Sorgenti: github.com/moji2002/1st-pouf e github.com/SolDevelo/soldevelo-registry, licenza dichiarata a monte «see source».
La categoria stretta non è una lacuna del catalogo: dice che «impostazioni» non è un tipo di pagina. Il blocco di 1st-pouf non dichiara categorie e si trova per titolo, non per filtro. Cerca per nome, apri il codice e leggi come tratta i campi.
Dati, non colonne
npx shadcn@latest add tabs switch label
import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch";
type Setting = {
id: string;
label: string;
description: string;
defaultOn: boolean;
};
export function SettingsSection({ title, settings }: { title: string; settings: Setting[] }) {
return (
{title}
{settings.map((setting) => (
{setting.label}
{setting.description}
))}
);
}
L'esempio concreto: la sezione è un oggetto
Tre file bastano. Un file di configurazione dichiara le sezioni, una route dinamica legge lo slug e il componente disegna le righe. Aggiungere «avvisi di sicurezza» costa una voce nell'array e una riga nella tabella dei valori; nessuna pagina viene copiata.
export type Field = {
id: string;
label: string;
help: string;
kind: "switch" | "select";
options?: string[];
dependsOn?: { field: string; equals: boolean };
permission: "member" | "admin";
};
export const sections = {
notifications: {
title: "Notifiche",
route: "/settings/notifications",
fields: [
{ id: "digest", label: "Riepilogo settimanale", help: "Una email il lunedì.", kind: "switch", permission: "member" },
{ id: "mentions", label: "Menzioni", help: "Solo quando qualcuno ti nomina.", kind: "switch", dependsOn: { field: "digest", equals: true }, permission: "member" },
{ id: "channel", label: "Canale", help: "Dove arriva il riepilogo.", kind: "select", options: ["email", "slack"], permission: "admin" },
],
},
} satisfies Record;
Il tipo Field è il contratto. Chi aggiunge un campo non tocca il JSX: sceglie kind, scrive help e dichiara permission. La dipendenza dependsOn spegne «Menzioni» finché il riepilogo è spento, e la ragione sta nel dato, non in un ramo nascosto.
Quando la pagina cresce
Con lo schema, una nuova impostazione è una voce nell'array e un campo nel database. Permessi, audit log e valori ereditati restano fuori dal componente: li decide il server. Se un campo dipende da un altro, la dipendenza sta nello schema, non in un if dentro il JSX.
Limite: entrambi i blocchi dichiarano «see source» come licenza, e settings di 1st-pouf porta quattordici registryDependencies. Non è un componente da copiare in cinque minuti: leggilo come riferimento di struttura e verifica la licenza prima di spedire.
La regola di decisione
Regola: un campo che richiede un permesso diverso dagli altri, o che dipende da un altro campo, prende una sezione con la sua route. Tutto il resto resta una riga nella lista. La soglia evita i due estremi: dodici tab in una pagina sola e una pagina per un singolo interruttore.
L'errore da evitare
L'errore da evitare è fidarsi di defaultChecked. Lo switch mostra il nuovo valore prima della risposta, e se il salvataggio fallisce nessuno lo riporta indietro: la pagina dichiara «attivo» un valore che il database non ha mai ricevuto. Tieni il valore nello stato, disegna lo switch da lì e ripristina lo stato precedente quando la risposta è un errore.
Per scegliere il blocco giusto leggi Migliori settings blocks 2026 e la checklist in 12 punti per componenti React accessibili. Se devi decidere se partire da un tema pronto, il confronto Temi shadcn gratis o premium mette i costi sul tavolo.
Domande frequenti
Una pagina sola o una route per sezione?
Una route per sezione, perché il link diretto è la funzione principale di una pagina impostazioni. Una pagina con dodici tab non si può linkare né ricaricare senza perdere il punto.
Autosave o pulsante Salva?
Pulsante Salva per sezione, salvo impostazioni banali come un toggle. L'autosave invisibile rende impossibile capire cosa è stato scritto e quando.
Come gestisco i campi disabilitati?
Con una spiegazione accanto al campo e un motivo leggibile, non solo un attributo disabled. Chi non capisce perché è bloccato apre un ticket.
Dove metto i permessi?
Sul server. Il client può nascondere un controllo, ma la decisione su chi può cambiare un valore va presa e verificata lato server.
Posso usare i blocchi settings del catalogo?
Puoi leggerli e adattarli, ma la licenza è dichiarata «see source». Controlla il repository indicato prima di includerli in un prodotto.