({
+ isOpen: true,
+ onChange: () => undefined,
+});
diff --git a/src/terra/setUpUI/provider/hook.ts b/src/terra/setUpUI/provider/hook.ts
new file mode 100644
index 00000000..5135d6cb
--- /dev/null
+++ b/src/terra/setUpUI/provider/hook.ts
@@ -0,0 +1,11 @@
+import { useContext } from "react";
+import { TerraSetUpUIContext } from "./context";
+import { TerraSetUpUIContextValue } from "./types";
+
+/**
+ * Returns the terra set-up UI context value.
+ * @returns terra set-up UI context value.
+ */
+export function useTerraSetUpUI(): TerraSetUpUIContextValue {
+ return useContext(TerraSetUpUIContext);
+}
diff --git a/src/terra/setUpUI/provider/provider.tsx b/src/terra/setUpUI/provider/provider.tsx
new file mode 100644
index 00000000..9c7dd3ca
--- /dev/null
+++ b/src/terra/setUpUI/provider/provider.tsx
@@ -0,0 +1,29 @@
+import { JSX, ReactNode } from "react";
+import { CollapseProvider } from "../../../components/common/Collapse/provider/provider";
+import { TerraSetUpUIContext } from "./context";
+
+/**
+ * Provides transient UI state for the `TerraSetUpForm` (e.g. collapsed/expanded).
+ * Mounted automatically inside `TerraProfileProvider`.
+ * @param props - Provider props.
+ * @param props.children - Children components.
+ * @returns terra set-up UI provider.
+ */
+export function TerraSetUpUIProvider({
+ children,
+}: {
+ children: ReactNode;
+}): JSX.Element {
+ return (
+
+ {({ isIn, onChange }) => {
+ const value = { isOpen: isIn, onChange };
+ return (
+
+ {children}
+
+ );
+ }}
+
+ );
+}
diff --git a/src/terra/setUpUI/provider/types.ts b/src/terra/setUpUI/provider/types.ts
new file mode 100644
index 00000000..de41f099
--- /dev/null
+++ b/src/terra/setUpUI/provider/types.ts
@@ -0,0 +1,12 @@
+/**
+ * Terra set-up UI context value. Tracks transient UI state for the
+ * `TerraSetUpForm` (e.g. whether the wizard is open or collapsed by the user).
+ *
+ * State is held in-memory in the provider, so it persists across SPA
+ * navigations within a session and resets on full page reload, new tab, or
+ * browser close. No browser storage is used.
+ */
+export interface TerraSetUpUIContextValue {
+ isOpen: boolean;
+ onChange: () => void;
+}
diff --git a/tests/terraSetUpUIProvider.test.tsx b/tests/terraSetUpUIProvider.test.tsx
new file mode 100644
index 00000000..5ea5244c
--- /dev/null
+++ b/tests/terraSetUpUIProvider.test.tsx
@@ -0,0 +1,52 @@
+import { act, render, screen } from "@testing-library/react";
+import { JSX } from "react";
+import { useTerraSetUpUI } from "../src/terra/setUpUI/provider/hook";
+import { TerraSetUpUIProvider } from "../src/terra/setUpUI/provider/provider";
+
+const TEST_ID_STATE = "terra-set-up-ui-state";
+const TEST_ID_TOGGLE = "terra-set-up-ui-toggle";
+const TEXT_OPEN = "open";
+const TEXT_CLOSED = "closed";
+
+function Consumer(): JSX.Element {
+ const { isOpen, onChange } = useTerraSetUpUI();
+ return (
+ <>
+ {isOpen ? TEXT_OPEN : TEXT_CLOSED}
+
+ >
+ );
+}
+
+describe("TerraSetUpUIProvider", () => {
+ it("renders children with the initial open state", () => {
+ render(
+
+
+ ,
+ );
+ expect(screen.getByTestId(TEST_ID_STATE).textContent).toEqual(TEXT_OPEN);
+ });
+
+ it("toggles isOpen when onChange is invoked", () => {
+ render(
+
+
+ ,
+ );
+
+ expect(screen.getByTestId(TEST_ID_STATE).textContent).toEqual(TEXT_OPEN);
+
+ act(() => {
+ screen.getByTestId(TEST_ID_TOGGLE).click();
+ });
+ expect(screen.getByTestId(TEST_ID_STATE).textContent).toEqual(TEXT_CLOSED);
+
+ act(() => {
+ screen.getByTestId(TEST_ID_TOGGLE).click();
+ });
+ expect(screen.getByTestId(TEST_ID_STATE).textContent).toEqual(TEXT_OPEN);
+ });
+});