diff --git a/ivette/src/frama-c/states.ts b/ivette/src/frama-c/states.ts
index 13644db3a403696352168de1b8cc81d216b26b53..ba057a54d120513be3521af918d643771e9d2970 100644
--- a/ivette/src/frama-c/states.ts
+++ b/ivette/src/frama-c/states.ts
@@ -37,6 +37,7 @@ import { GlobalState, useGlobalState } from 'dome/data/states';
 import { Client, useModel } from 'dome/table/models';
 import { CompactModel } from 'dome/table/arrays';
 import * as Ast from 'frama-c/kernel/api/ast';
+import { FieldState, FieldError } from 'dome/layout/forms';
 import * as Server from './server';
 
 // --------------------------------------------------------------------------
@@ -307,6 +308,34 @@ export function useSyncValue<A>(value: Value<A>): A | undefined {
   return v;
 }
 
+/** Synchronize FieldState and server state only if there is no error. */
+export function useServerField<A>(
+  state: State<A>,
+  defaultValue: A,
+): FieldState<A> {
+  const [value, setState] = useSyncState(state);
+  const [localValue, setLocalValue] = React.useState(value);
+  const [localError, setLocalError] = React.useState<FieldError>(undefined);
+
+  React.useEffect(() => {
+    !localError && setLocalValue(value);
+  }, [value, localError]);
+
+  const update = React.useCallback((newValue: A, newError: FieldError) => {
+    setLocalValue(newValue);
+    setLocalError(newError);
+    if (!newError) {
+      setState(newValue);
+    }
+  }, [setState]);
+
+  return {
+    value: localValue ?? defaultValue,
+    error: localError,
+    onChanged: update
+  };
+}
+
 // --------------------------------------------------------------------------
 // --- Synchronized Arrays
 // --------------------------------------------------------------------------