Portal Community

Hook Return Type

import { useFormVisibility } from '@atlas-forms/player-components-react';

interface FormVisibilityState {
  /**
   * Returns true when the control should be rendered.
   * Evaluates both modeVisibilitySettings and visibilityRule.
   */
  isVisible: (controlId: string) => boolean;

  /**
   * Returns all controls for which isVisible() returns true.
   * Result is recalculated on every value change.
   */
  getVisibleControls: () => FormControl[];
}

isVisible(controlId)

const { isVisible } = useFormVisibility();

// Usage — filter before rendering
const controls = schema.controls ?? [];

return (
  <div>
    {controls
      .filter(c => isVisible(c.id))
      .map(c => <ControlRenderer key={c.id} control={c} />)
    }
  </div>
);

getVisibleControls()

const { getVisibleControls } = useFormVisibility();

// Usage — progress calculation
const visibleControls = getVisibleControls();
const filled  = visibleControls.filter(c => {
  const v = values[c.id];
  return v !== undefined && v !== null && v !== '';
}).length;

const progress = Math.round((filled / visibleControls.length) * 100);

Section-Level Filtering Pattern

For grouped layouts, filter at the section level to avoid rendering empty sections:

const { isVisible } = useFormVisibility();
const schema = engine.getSchema();

return (
  <>
    {(schema.sections ?? []).map(section => {
      // Filter controls for this section
      const controls = (schema.controls ?? [])
        .filter(c => c.sectionId === section.id && isVisible(c.id));

      // Hide the entire section if no controls are visible
      if (controls.length === 0) return null;

      return (
        <div key={section.id} className="section">
          <h3>{section.title}</h3>
          {controls.map(c => <ControlRenderer key={c.id} control={c} />)}
        </div>
      );
    })}
  </>
);

Hook Rules

RuleDetail
Must be inside FormStateProviderThrows if called outside the provider context
Call once per componentDo not call inside a loop or conditionally — call at the top of the component and pass isVisible down
Re-renders on value changesThe hook subscribes to the form context; the component re-renders when any value changes
isVisible() is memoisedThe function reference is stable; safe to pass as a prop without causing child re-renders from reference inequality
ControlRenderer Checks Visibility Internally ControlRenderer already calls isVisible() internally and returns null if the control is not visible. If you are using ControlRenderer, you do not need to pre-filter with isVisible() — it is done for you. Only use isVisible() directly when you need to make layout decisions at the section or container level (e.g., hiding an empty section card).