diff --git a/.gitignore b/.gitignore
index b168fc06..7f174c0f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,4 @@ coverage/
 html/
 node_modules/
 NOTES.md
+tsconfig.tsbuildinfo
diff --git a/apps/spotlight/src/components/AppFormManager.tsx b/apps/spotlight/src/components/AppFormManager.tsx
index 020464bc..ad669db0 100644
--- a/apps/spotlight/src/components/AppFormManager.tsx
+++ b/apps/spotlight/src/components/AppFormManager.tsx
@@ -1,9 +1,19 @@
 import React from 'react';
 
-import { FormManager } from '@atj/design';
+import { FormManager, defaultFormElementComponent } from '@atj/design';
+
 import { getAppContext } from '../context';
 
 export default function () {
   const ctx = getAppContext();
-  return <FormManager formService={ctx.formService} baseUrl={ctx.baseUrl} />;
+  return (
+    <FormManager
+      context={{
+        config: ctx.formConfig,
+        components: defaultFormElementComponent,
+      }}
+      formService={ctx.formService}
+      baseUrl={ctx.baseUrl}
+    />
+  );
 }
diff --git a/apps/spotlight/src/components/AppFormRouter.tsx b/apps/spotlight/src/components/AppFormRouter.tsx
index 09cbc11d..9cff368f 100644
--- a/apps/spotlight/src/components/AppFormRouter.tsx
+++ b/apps/spotlight/src/components/AppFormRouter.tsx
@@ -5,5 +5,5 @@ import { getAppContext } from '../context';
 
 export default function AppFormRouter() {
   const ctx = getAppContext();
-  return <FormRouter formService={ctx.formService} />;
+  return <FormRouter config={ctx.formConfig} formService={ctx.formService} />;
 }
diff --git a/apps/spotlight/src/components/Footer.astro b/apps/spotlight/src/components/Footer.astro
index 02a2d1c7..f11a3421 100644
--- a/apps/spotlight/src/components/Footer.astro
+++ b/apps/spotlight/src/components/Footer.astro
@@ -18,25 +18,14 @@ const { github } = Astro.props;
         <nav class="usa-footer__nav" aria-label="Footer navigation,">
           <ul class="grid-row grid-gap">
             <li
-              class="
-            mobile-lg:grid-col-6
-            desktop:grid-col-auto
-            usa-footer__primary-content
-          "
+              class="mobile-lg:grid-col-6 desktop:grid-col-auto usa-footer__primary-content"
             >
-              <a
-                class="usa-footer__primary-link"
-                href="https://10x.gsa.gov/"
-              >
+              <a class="usa-footer__primary-link" href="https://10x.gsa.gov/">
                 10x
               </a>
             </li>
             <li
-              class="
-            mobile-lg:grid-col-6
-            desktop:grid-col-auto
-            usa-footer__primary-content
-          "
+              class="mobile-lg:grid-col-6 desktop:grid-col-auto usa-footer__primary-content"
             >
               <a
                 class="usa-footer__primary-link"
diff --git a/apps/spotlight/src/context.ts b/apps/spotlight/src/context.ts
index 32e8c230..12330e79 100644
--- a/apps/spotlight/src/context.ts
+++ b/apps/spotlight/src/context.ts
@@ -1,13 +1,17 @@
+import { FormConfig } from '@atj/forms';
+import { defaultFormConfig } from '@atj/forms';
 import {
   type FormService,
   createBrowserFormService,
   createTestFormService,
 } from '@atj/form-service';
+
 import { type GithubRepository } from './lib/github';
 
 export type AppContext = {
   baseUrl: `${string}/`;
   github: GithubRepository;
+  formConfig: FormConfig;
   formService: FormService;
 };
 
@@ -24,6 +28,7 @@ const createAppContext = (env: any) => {
   return {
     github: env.GITHUB,
     baseUrl: env.BASE_URL,
+    formConfig: defaultFormConfig,
     formService: createAppFormService(),
   };
 };
diff --git a/package.json b/package.json
index aa2f1d94..a3aad8cf 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
   },
   "devDependencies": {
     "@types/node": "^20.11.16",
+    "@vitest/coverage-c8": "^0.33.0",
     "@vitest/coverage-v8": "^1.2.2",
     "@vitest/ui": "^1.2.2",
     "eslint": "^8.56.0",
diff --git a/packages/design/.eslintrc.cjs b/packages/design/.eslintrc.cjs
index 1e1167f2..0d5fa822 100644
--- a/packages/design/.eslintrc.cjs
+++ b/packages/design/.eslintrc.cjs
@@ -1,35 +1,31 @@
 module.exports = {
-    "env": {
-        "browser": true,
-        "es2021": true
+  env: {
+    browser: true,
+    es2021: true,
+  },
+  extends: [
+    'eslint:recommended',
+    'plugin:@typescript-eslint/recommended',
+    'plugin:react/recommended',
+  ],
+  overrides: [
+    {
+      env: {
+        node: true,
+      },
+      files: ['.eslintrc.{js,cjs}'],
+      parserOptions: {
+        sourceType: 'script',
+      },
     },
-    "extends": [
-        "eslint:recommended",
-        "plugin:@typescript-eslint/recommended",
-        "plugin:react/recommended"
-    ],
-    "overrides": [
-        {
-            "env": {
-                "node": true
-            },
-            "files": [
-                ".eslintrc.{js,cjs}"
-            ],
-            "parserOptions": {
-                "sourceType": "script"
-            }
-        }
-    ],
-    "parser": "@typescript-eslint/parser",
-    "parserOptions": {
-        "ecmaVersion": "latest",
-        "sourceType": "module"
-    },
-    "plugins": [
-        "@typescript-eslint",
-        "react"
-    ],
-    "rules": {
-    }
-}
+  ],
+  parser: '@typescript-eslint/parser',
+  parserOptions: {
+    ecmaVersion: 'latest',
+    sourceType: 'module',
+  },
+  plugins: ['@typescript-eslint', 'react'],
+  rules: {
+    'react/prop-types': 'off',
+  },
+};
diff --git a/packages/design/src/Form/Form.stories.tsx b/packages/design/src/Form/Form.stories.tsx
index e2713a34..a8e834a9 100644
--- a/packages/design/src/Form/Form.stories.tsx
+++ b/packages/design/src/Form/Form.stories.tsx
@@ -2,13 +2,14 @@ import React from 'react';
 import type { Meta, StoryObj } from '@storybook/react';
 
 import Form from '.';
-import { createTestForm } from '../test-form';
+import { createTestForm, createTestFormConfig } from '../test-form';
 
 export default {
   title: 'Form',
   component: Form,
   decorators: [(Story, args) => <Story {...args} />],
   args: {
+    config: createTestFormConfig(),
     form: createTestForm(),
   },
   tags: ['autodocs'],
diff --git a/packages/design/src/Form/index.tsx b/packages/design/src/Form/index.tsx
index 33c40f5a..77e13fca 100644
--- a/packages/design/src/Form/index.tsx
+++ b/packages/design/src/Form/index.tsx
@@ -4,6 +4,7 @@ import { FormProvider, useForm } from 'react-hook-form';
 import {
   createFormSession,
   createPrompt,
+  type FormConfig,
   type FormDefinition,
 } from '@atj/forms';
 
@@ -11,14 +12,16 @@ import PromptSegment from './PromptSegment';
 import ActionBar from './ActionBar';
 
 export default function Form({
+  config,
   form,
   onSubmit,
 }: {
+  config: FormConfig;
   form: FormDefinition;
   onSubmit?: (data: Record<string, string>) => void;
 }) {
   const session = createFormSession(form);
-  const prompt = createPrompt(session);
+  const prompt = createPrompt(config, session);
 
   const formMethods = useForm<Record<string, string>>({});
   return (
diff --git a/packages/design/src/FormManager/DocumentImporter/index.tsx b/packages/design/src/FormManager/DocumentImporter/index.tsx
index c06c1536..db71c672 100644
--- a/packages/design/src/FormManager/DocumentImporter/index.tsx
+++ b/packages/design/src/FormManager/DocumentImporter/index.tsx
@@ -3,7 +3,11 @@ import { useNavigate } from 'react-router-dom';
 
 import { addDocument, addDocumentFieldsToForm } from '@atj/documents';
 import { type FormService } from '@atj/form-service';
-import { type DocumentFieldMap, type FormDefinition } from '@atj/forms';
+import {
+  type FormConfig,
+  type DocumentFieldMap,
+  type FormDefinition,
+} from '@atj/forms';
 
 import { onFileInputChangeGetFile } from '../FormList/PDFFileSelect/file-input';
 import Form from '../../Form';
@@ -11,11 +15,13 @@ import Form from '../../Form';
 const DocumentImporter = ({
   baseUrl,
   formId,
+  config,
   form,
   formService,
 }: {
   baseUrl: string;
   formId: string;
+  config: FormConfig;
   form: FormDefinition;
   formService: FormService;
 }) => {
@@ -146,6 +152,7 @@ const DocumentImporter = ({
     return (
       <>
         <Form
+          config={config}
           form={previewForm}
           onSubmit={data => {
             //handleFormSubmission(formId, data);
diff --git a/packages/design/src/FormManager/FormEdit/FormEdit.stories.tsx b/packages/design/src/FormManager/FormEdit/FormEdit.stories.tsx
index 852b86e8..7ed8dffd 100644
--- a/packages/design/src/FormManager/FormEdit/FormEdit.stories.tsx
+++ b/packages/design/src/FormManager/FormEdit/FormEdit.stories.tsx
@@ -5,7 +5,7 @@ import type { Meta, StoryObj } from '@storybook/react';
 import { createTestFormService } from '@atj/form-service';
 
 import FormEdit from '.';
-import { createTestForm } from '../../test-form';
+import { createTestForm, createTestFormContext } from '../../test-form';
 
 export default {
   title: 'FormManager/FormEdit',
@@ -18,6 +18,7 @@ export default {
     ),
   ],
   args: {
+    context: createTestFormContext(),
     formId: 'test-form',
     formService: createTestFormService({
       'test-form': createTestForm(),
diff --git a/packages/design/src/FormManager/FormEdit/FormElementEdit/RenderField.tsx b/packages/design/src/FormManager/FormEdit/FormElementEdit/RenderField.tsx
deleted file mode 100644
index 26341ac4..00000000
--- a/packages/design/src/FormManager/FormEdit/FormElementEdit/RenderField.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-import React from 'react';
-import { useFormContext } from 'react-hook-form';
-
-import { FormDefinition, FormElement } from '@atj/forms';
-import { SequenceElementEdit } from './SequenceElementEdit';
-
-export default function RenderField({
-  form,
-  element,
-}: {
-  form: FormDefinition;
-  element: FormElement;
-}) {
-  const { register } = useFormContext();
-  const fieldId = element.id;
-  if (element.type === 'input') {
-    return (
-      <div className="grid-row grid-gap">
-        <div className="grid-col">
-          <label className="usa-label">
-            Input type
-            <select className="usa-select" {...register(`${fieldId}.type`)}>
-              <option value={'input'}>Input</option>
-              <option value={'textarea'}>Textarea</option>
-            </select>
-          </label>
-        </div>
-        <div className="grid-col">
-          <label className="usa-label">
-            Field label
-            <input
-              className="usa-input"
-              {...register(`${fieldId}.text`)}
-              type="text"
-            ></input>
-          </label>
-        </div>
-        <div className="grid-col">
-          <label className="usa-label">
-            Default value
-            <input
-              className="usa-input"
-              type="text"
-              {...register(`${fieldId}.initial`)}
-            ></input>
-          </label>
-        </div>
-        <div className="grid-col">
-          <div className="usa-checkbox">
-            <input
-              className="usa-checkbox__input"
-              type="checkbox"
-              id={`${fieldId}.required`}
-              {...register(`${fieldId}.required`)}
-            />
-            <label
-              className="usa-checkbox__label"
-              htmlFor={`${fieldId}.required`}
-            >
-              Required
-            </label>
-          </div>
-        </div>
-      </div>
-    );
-  } else if (element.type == 'sequence') {
-    return (
-      <fieldset>
-        <SequenceElementEdit element={element} form={form} />
-      </fieldset>
-    );
-  } else {
-    const _exhaustiveCheck: never = element;
-    return _exhaustiveCheck;
-  }
-}
diff --git a/packages/design/src/FormManager/FormEdit/FormElementEdit/SequenceElementEdit.tsx b/packages/design/src/FormManager/FormEdit/FormElementEdit/SequenceElementEdit.tsx
deleted file mode 100644
index 8a55f6de..00000000
--- a/packages/design/src/FormManager/FormEdit/FormElementEdit/SequenceElementEdit.tsx
+++ /dev/null
@@ -1,111 +0,0 @@
-import React, { useState } from 'react';
-import {
-  DndContext,
-  closestCenter,
-  KeyboardSensor,
-  PointerSensor,
-  useSensor,
-  useSensors,
-} from '@dnd-kit/core';
-import {
-  arrayMove,
-  SortableContext,
-  sortableKeyboardCoordinates,
-  useSortable,
-  verticalListSortingStrategy,
-} from '@dnd-kit/sortable';
-import { CSS } from '@dnd-kit/utilities';
-
-import { type FormDefinition, type FormElement } from '@atj/forms';
-import { type SequenceElement } from '@atj/forms/src/elements/sequence';
-
-import RenderField from './RenderField';
-import { useFormContext } from 'react-hook-form';
-
-interface ItemProps {
-  id: string;
-  form: FormDefinition;
-  element: FormElement;
-}
-
-const SortableItem = ({ id, form, element }: ItemProps) => {
-  const { attributes, listeners, setNodeRef, transform, transition } =
-    useSortable({ id });
-
-  const style = {
-    transform: CSS.Transform.toString(transform),
-    transition,
-  };
-
-  return (
-    <li ref={setNodeRef} style={style}>
-      <div {...listeners} {...attributes} style={{ cursor: 'grab' }}>
-        :::
-      </div>
-      <RenderField key={element.id} element={element} form={form} />
-    </li>
-  );
-};
-
-export const SequenceElementEdit = ({
-  element,
-  form,
-}: {
-  element: SequenceElement;
-  form: FormDefinition;
-}) => {
-  const { register, setValue } = useFormContext();
-  const [elements, setElements] = useState(
-    element.elements.map(elementId => {
-      return form.elements[elementId];
-    })
-  );
-  const sensors = useSensors(
-    useSensor(PointerSensor),
-    useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
-  );
-
-  return (
-    <DndContext
-      sensors={sensors}
-      collisionDetection={closestCenter}
-      onDragEnd={event => {
-        const { active, over } = event;
-        if (over === null) {
-          return;
-        }
-        if (active.id !== over.id) {
-          const oldIndex = elements.findIndex(element => {
-            return element.id === active.id;
-          });
-          const newIndex = elements.findIndex(element => {
-            return element.id === over.id;
-          });
-          const newOrder = arrayMove(elements, oldIndex, newIndex);
-          setElements(newOrder);
-          setValue(element.id, {
-            id: element.id,
-            type: element.type,
-            elements: newOrder.map(element => element.id),
-          });
-        }
-      }}
-    >
-      <SortableContext items={elements} strategy={verticalListSortingStrategy}>
-        <ul>
-          <input type="hidden" {...register(`${element.id}.id`)} />
-          <input type="hidden" {...register(`${element.id}.type`)} />
-          <input type="hidden" {...register(`${element.id}.elements`)} />
-          {elements.map(elements => (
-            <SortableItem
-              key={elements.id}
-              id={elements.id}
-              element={elements}
-              form={form}
-            />
-          ))}
-        </ul>
-      </SortableContext>
-    </DndContext>
-  );
-};
diff --git a/packages/design/src/FormManager/FormEdit/index.tsx b/packages/design/src/FormManager/FormEdit/index.tsx
index 8bd4cffc..3f915f46 100644
--- a/packages/design/src/FormManager/FormEdit/index.tsx
+++ b/packages/design/src/FormManager/FormEdit/index.tsx
@@ -10,12 +10,14 @@ import {
   updateElements,
 } from '@atj/forms';
 
-import RenderField from './FormElementEdit/RenderField';
+import { type FormUIContext } from '../../config';
 
 export default function FormEdit({
+  context,
   formId,
   formService,
 }: {
+  context: FormUIContext;
   formId: string;
   formService: FormService;
 }) {
@@ -40,6 +42,7 @@ export default function FormEdit({
         </li>
       </ul>
       <EditForm
+        context={context}
         form={form}
         onSave={form => formService.saveForm(formId, form)}
       />
@@ -47,54 +50,30 @@ export default function FormEdit({
   );
 }
 
-// FIXME: Once we clean up the input type, this function should be unnecessary.
-const getFormFieldMap = (elements: FormElementMap) => {
-  return Object.values(elements).reduce((acc, element) => {
-    if (element.type === 'input') {
-      acc[element.id] = {
-        type: 'input',
-        id: element.id,
-        text: element.text,
-        initial: element.initial.toString(),
-        required: element.required,
-      };
-      return acc;
-    } else if (element.type === 'sequence') {
-      acc[element.id] = {
-        type: 'sequence',
-        id: element.id,
-        elements: element.elements,
-      };
-      return acc;
-    } else {
-      const _exhaustiveCheck: never = element;
-      return _exhaustiveCheck;
-    }
-  }, {} as FormElementMap);
-};
-
 const EditForm = ({
+  context,
   form,
   onSave,
 }: {
+  context: FormUIContext;
   form: FormDefinition;
   onSave: (form: FormDefinition) => void;
 }) => {
-  const formElements: FormElementMap = getFormFieldMap(form.elements);
   const methods = useForm<FormElementMap>({
-    defaultValues: formElements,
+    defaultValues: form.elements,
   });
   const rootField = getRootFormElement(form);
+  const Component = context.components[rootField.type];
   return (
     <FormProvider {...methods}>
       <form
         onSubmit={methods.handleSubmit(data => {
-          const updatedForm = updateElements(form, data);
+          const updatedForm = updateElements(context.config, form, data);
           onSave(updatedForm);
         })}
       >
         <ButtonBar />
-        <RenderField form={form} element={rootField} />
+        <Component context={context} form={form} element={rootField} />
         <ButtonBar />
       </form>
     </FormProvider>
diff --git a/packages/design/src/FormManager/FormPreview/FormPreview.stories.tsx b/packages/design/src/FormManager/FormPreview/FormPreview.stories.tsx
index 31d0eb7d..9c79a065 100644
--- a/packages/design/src/FormManager/FormPreview/FormPreview.stories.tsx
+++ b/packages/design/src/FormManager/FormPreview/FormPreview.stories.tsx
@@ -5,7 +5,7 @@ import type { Meta, StoryObj } from '@storybook/react';
 import { createTestFormService } from '@atj/form-service';
 
 import { FormViewById } from '.';
-import { createTestForm } from '../../test-form';
+import { createTestForm, createTestFormConfig } from '../../test-form';
 
 export default {
   title: 'FormManager/FormView',
@@ -18,6 +18,7 @@ export default {
     ),
   ],
   args: {
+    config: createTestFormConfig(),
     formId: 'test-form',
     formService: createTestFormService({
       'test-form': createTestForm(),
diff --git a/packages/design/src/FormManager/FormPreview/index.tsx b/packages/design/src/FormManager/FormPreview/index.tsx
index 09e89d90..e18f7beb 100644
--- a/packages/design/src/FormManager/FormPreview/index.tsx
+++ b/packages/design/src/FormManager/FormPreview/index.tsx
@@ -1,12 +1,16 @@
 import React from 'react';
 
+import { type FormConfig } from '@atj/forms';
 import { type FormService } from '@atj/form-service';
+
 import Form from '../../Form';
 
 export const FormViewById = ({
+  config,
   formId,
   formService,
 }: {
+  config: FormConfig;
   formId: string;
   formService: FormService;
 }) => {
@@ -18,6 +22,7 @@ export const FormViewById = ({
 
   return (
     <Form
+      config={config}
       form={result.data}
       onSubmit={async data => {
         const submission = await formService.submitForm(formId, data);
diff --git a/packages/design/src/FormManager/import-document.tsx b/packages/design/src/FormManager/import-document.tsx
index 7ca140cc..3a609969 100644
--- a/packages/design/src/FormManager/import-document.tsx
+++ b/packages/design/src/FormManager/import-document.tsx
@@ -1,14 +1,18 @@
 import React from 'react';
+
+import { FormConfig } from '@atj/forms';
 import { type FormService } from '@atj/form-service';
 
 import DocumentImporter from './DocumentImporter';
 
 export const FormDocumentImport = ({
   baseUrl,
+  config,
   formId,
   formService,
 }: {
   baseUrl: string;
+  config: FormConfig;
   formId: string;
   formService: FormService;
 }) => {
@@ -19,6 +23,7 @@ export const FormDocumentImport = ({
   }
   return (
     <DocumentImporter
+      config={config}
       formService={formService}
       baseUrl={baseUrl}
       formId={formId}
diff --git a/packages/design/src/FormManager/index.tsx b/packages/design/src/FormManager/index.tsx
index f3492f39..075b42e3 100644
--- a/packages/design/src/FormManager/index.tsx
+++ b/packages/design/src/FormManager/index.tsx
@@ -8,11 +8,14 @@ import FormEdit from './FormEdit';
 import FormList from './FormList';
 import { FormViewById } from './FormPreview';
 import { FormDocumentImport } from './import-document';
+import { FormUIContext } from '../config';
 
 export default function FormManager({
+  context,
   baseUrl,
   formService,
 }: {
+  context: FormUIContext;
   baseUrl: string;
   formService: FormService;
 }) {
@@ -32,7 +35,13 @@ export default function FormManager({
             if (formId === undefined) {
               return <div>formId is undefined</div>;
             }
-            return <FormViewById formId={formId} formService={formService} />;
+            return (
+              <FormViewById
+                config={context.config}
+                formId={formId}
+                formService={formService}
+              />
+            );
           }}
         />
         <Route
@@ -42,7 +51,13 @@ export default function FormManager({
             if (formId === undefined) {
               return <div>formId is undefined</div>;
             }
-            return <FormEdit formId={formId} formService={formService} />;
+            return (
+              <FormEdit
+                context={context}
+                formId={formId}
+                formService={formService}
+              />
+            );
           }}
         />
         <Route
@@ -64,6 +79,7 @@ export default function FormManager({
             }
             return (
               <FormDocumentImport
+                config={context.config}
                 baseUrl={baseUrl}
                 formId={formId}
                 formService={formService}
diff --git a/packages/design/src/FormRouter/index.tsx b/packages/design/src/FormRouter/index.tsx
index ceda5627..b59082a4 100644
--- a/packages/design/src/FormRouter/index.tsx
+++ b/packages/design/src/FormRouter/index.tsx
@@ -3,11 +3,14 @@ import { useParams, HashRouter, Route, Routes } from 'react-router-dom';
 
 import { type FormService } from '@atj/form-service';
 import Form from '../Form';
+import { type FormConfig } from '@atj/forms';
 
 // Wrapper around Form that includes a client-side router for loading forms.
 export default function FormRouter({
+  config,
   formService,
 }: {
+  config: FormConfig;
   formService: FormService;
 }) {
   return (
@@ -33,6 +36,7 @@ export default function FormRouter({
             }
             return (
               <Form
+                config={config}
                 form={result.data}
                 onSubmit={async data => {
                   const submission = await formService.submitForm(formId, data);
diff --git a/packages/design/src/config/InputElementEdit.tsx b/packages/design/src/config/InputElementEdit.tsx
new file mode 100644
index 00000000..3e6a2e0c
--- /dev/null
+++ b/packages/design/src/config/InputElementEdit.tsx
@@ -0,0 +1,61 @@
+import React from 'react';
+import { useFormContext } from 'react-hook-form';
+
+import { type InputElement } from '@atj/forms/src/config/elements/input';
+
+import { type FormElementComponent } from '.';
+
+const InputElementEdit: FormElementComponent<InputElement> = ({ element }) => {
+  const { register } = useFormContext();
+  return (
+    <div className="grid-row grid-gap">
+      <div className="grid-col">
+        <label className="usa-label">
+          Input type
+          <select className="usa-select" {...register(`${element.id}.type`)}>
+            <option value={'input'}>Input</option>
+            <option value={'textarea'}>Textarea</option>
+          </select>
+        </label>
+      </div>
+      <div className="grid-col">
+        <label className="usa-label">
+          Field label
+          <input
+            className="usa-input"
+            {...register(`${element.id}.data.text`)}
+            type="text"
+          ></input>
+        </label>
+      </div>
+      <div className="grid-col">
+        <label className="usa-label">
+          Default value
+          <input
+            className="usa-input"
+            type="text"
+            {...register(`${element.id}.data.initial`)}
+          ></input>
+        </label>
+      </div>
+      <div className="grid-col">
+        <div className="usa-checkbox">
+          <input
+            className="usa-checkbox__input"
+            type="checkbox"
+            id={`${element.id}.data.required`}
+            {...register(`${element.id}.data.required`)}
+          />
+          <label
+            className="usa-checkbox__label"
+            htmlFor={`${element.id}.data.required`}
+          >
+            Required
+          </label>
+        </div>
+      </div>
+    </div>
+  );
+};
+
+export default InputElementEdit;
diff --git a/packages/design/src/config/SequenceElementEdit.tsx b/packages/design/src/config/SequenceElementEdit.tsx
new file mode 100644
index 00000000..40846fef
--- /dev/null
+++ b/packages/design/src/config/SequenceElementEdit.tsx
@@ -0,0 +1,127 @@
+import React, { useState } from 'react';
+import {
+  DndContext,
+  closestCenter,
+  KeyboardSensor,
+  PointerSensor,
+  useSensor,
+  useSensors,
+} from '@dnd-kit/core';
+import {
+  arrayMove,
+  SortableContext,
+  sortableKeyboardCoordinates,
+  useSortable,
+  verticalListSortingStrategy,
+} from '@dnd-kit/sortable';
+import { CSS } from '@dnd-kit/utilities';
+import { useFormContext } from 'react-hook-form';
+
+import { type FormDefinition, type FormElement } from '@atj/forms';
+import { type SequenceElement } from '@atj/forms/src/config/elements/sequence';
+
+import { type FormElementComponent, type FormUIContext } from '.';
+
+interface ItemProps<T> {
+  id: string;
+  form: FormDefinition;
+  element: FormElement<T>;
+  context: FormUIContext;
+}
+
+const SortableItem = <T,>({ id, form, element, context }: ItemProps<T>) => {
+  const { attributes, listeners, setNodeRef, transform, transition } =
+    useSortable({ id });
+
+  const style = {
+    transform: CSS.Transform.toString(transform),
+    transition,
+  };
+
+  const Component = context.components[element.type];
+
+  return (
+    <li ref={setNodeRef} style={style}>
+      <div {...listeners} {...attributes} style={{ cursor: 'grab' }}>
+        :::
+      </div>
+      <Component
+        key={element.id}
+        context={context}
+        element={element}
+        form={form}
+      />
+    </li>
+  );
+};
+
+const SequenceElementEdit: FormElementComponent<SequenceElement> = ({
+  context,
+  form,
+  element,
+}) => {
+  const { register, setValue } = useFormContext();
+  const [elements, setElements] = useState(
+    element.data.elements.map(elementId => {
+      return form.elements[elementId];
+    })
+  );
+  const sensors = useSensors(
+    useSensor(PointerSensor),
+    useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
+  );
+
+  return (
+    <fieldset>
+      <DndContext
+        sensors={sensors}
+        collisionDetection={closestCenter}
+        onDragEnd={event => {
+          const { active, over } = event;
+          if (over === null) {
+            return;
+          }
+          if (active.id !== over.id) {
+            const oldIndex = elements.findIndex(element => {
+              return element.id === active.id;
+            });
+            const newIndex = elements.findIndex(element => {
+              return element.id === over.id;
+            });
+            const newOrder = arrayMove(elements, oldIndex, newIndex);
+            setElements(newOrder);
+            setValue(element.id, {
+              id: element.id,
+              type: element.type,
+              data: {
+                elements: newOrder.map(element => element.id),
+              },
+            } satisfies SequenceElement);
+          }
+        }}
+      >
+        <SortableContext
+          items={elements}
+          strategy={verticalListSortingStrategy}
+        >
+          <ul>
+            <input type="hidden" {...register(`${element.id}.id`)} />
+            <input type="hidden" {...register(`${element.id}.type`)} />
+            <input type="hidden" {...register(`${element.id}`)} />
+            {elements.map(elements => (
+              <SortableItem
+                key={elements.id}
+                id={elements.id}
+                context={context}
+                element={elements}
+                form={form}
+              />
+            ))}
+          </ul>
+        </SortableContext>
+      </DndContext>
+    </fieldset>
+  );
+};
+
+export default SequenceElementEdit;
diff --git a/packages/design/src/config/index.ts b/packages/design/src/config/index.ts
new file mode 100644
index 00000000..0e43e4c3
--- /dev/null
+++ b/packages/design/src/config/index.ts
@@ -0,0 +1,25 @@
+import React from 'react';
+
+import { FormElement, type FormConfig, type FormDefinition } from '@atj/forms';
+
+import InputElementEdit from './InputElementEdit';
+import SequenceElementEdit from './SequenceElementEdit';
+
+export type FormUIContext = {
+  config: FormConfig;
+  components: ComponentForFormElement;
+};
+
+export type FormElementComponent<T extends FormElement> = React.ComponentType<{
+  context: FormUIContext;
+  form: FormDefinition;
+  element: T;
+}>;
+
+export type ComponentForFormElement<T extends FormElement = FormElement> =
+  Record<string, FormElementComponent<T>>;
+
+export const defaultFormElementComponent: ComponentForFormElement = {
+  sequence: SequenceElementEdit,
+  input: InputElementEdit,
+};
diff --git a/packages/design/src/index.ts b/packages/design/src/index.ts
index ed2bba96..463d805b 100644
--- a/packages/design/src/index.ts
+++ b/packages/design/src/index.ts
@@ -4,3 +4,4 @@ export { default as AvailableFormList } from './AvailableFormList';
 export { default as Form } from './Form';
 export { default as FormRouter } from './FormRouter';
 export { default as FormManager } from './FormManager';
+export * from './config';
diff --git a/packages/design/src/test-form.ts b/packages/design/src/test-form.ts
index 444014a1..94ad89cd 100644
--- a/packages/design/src/test-form.ts
+++ b/packages/design/src/test-form.ts
@@ -1,4 +1,6 @@
-import { createForm } from '@atj/forms';
+import { createForm, defaultFormConfig } from '@atj/forms';
+
+import { defaultFormElementComponent } from './config';
 
 export const createTestForm = () => {
   return createForm(
@@ -12,23 +14,44 @@ export const createTestForm = () => {
         {
           type: 'sequence',
           id: 'root',
-          elements: ['element-1', 'element-2'],
+          data: {
+            elements: ['element-1', 'element-2'],
+          },
         },
         {
           type: 'input',
           id: 'element-1',
-          text: 'FormElement 1',
-          initial: '',
-          required: true,
+          data: {
+            text: 'FormElement 1',
+            required: true,
+            initial: '',
+          },
         },
         {
           type: 'input',
           id: 'element-2',
-          text: 'FormElement 2',
-          initial: 'initial value',
-          required: false,
+          data: {
+            text: 'FormElement 2',
+            required: false,
+            initial: 'test',
+          },
         },
       ],
     }
   );
 };
+
+export const createTestFormConfig = () => {
+  return defaultFormConfig;
+};
+
+export const createTestFormElementComponentMap = () => {
+  return defaultFormElementComponent;
+};
+
+export const createTestFormContext = () => {
+  return {
+    config: defaultFormConfig,
+    components: defaultFormElementComponent,
+  };
+};
diff --git a/packages/design/src/test-helper.ts b/packages/design/src/test-helper.ts
index 29ced1c1..b4b0d339 100644
--- a/packages/design/src/test-helper.ts
+++ b/packages/design/src/test-helper.ts
@@ -1,7 +1,7 @@
-import { Store_CSFExports } from '@storybook/types';
-import { ReactRenderer, composeStories } from '@storybook/react';
+import { type Store_CSFExports } from '@storybook/types';
+import { type ReactRenderer, composeStories } from '@storybook/react';
 import { render } from '@testing-library/react';
-import { Entries } from 'type-fest';
+import { type Entries } from 'type-fest';
 import { describe, test } from 'vitest';
 
 /**
diff --git a/packages/documents/src/document.ts b/packages/documents/src/document.ts
index e17439fa..c09874f6 100644
--- a/packages/documents/src/document.ts
+++ b/packages/documents/src/document.ts
@@ -40,39 +40,47 @@ export const addDocumentFieldsToForm = (
   form: FormDefinition,
   fields: DocumentFieldMap
 ) => {
-  const elements: FormElement[] = [];
+  const elements: FormElement<any>[] = [];
   Object.entries(fields).map(([key, field]) => {
     if (field.type === 'CheckBox') {
       elements.push({
         type: 'input',
         id: field.name,
-        text: field.label,
-        initial: field.value,
-        required: field.required,
+        data: {
+          text: field.label,
+          initial: field.value,
+          required: field.required,
+        },
       });
     } else if (field.type === 'OptionList') {
       elements.push({
         type: 'input',
         id: field.name,
-        text: field.label,
-        initial: field.value,
-        required: field.required,
+        data: {
+          text: field.label,
+          initial: field.value,
+          required: field.required,
+        },
       });
     } else if (field.type === 'Dropdown') {
       elements.push({
         type: 'input',
         id: field.name,
-        text: field.label,
-        initial: field.value,
-        required: field.required,
+        data: {
+          text: field.label,
+          initial: field.value,
+          required: field.required,
+        },
       });
     } else if (field.type === 'TextField') {
       elements.push({
         type: 'input',
         id: field.name,
-        text: field.label,
-        initial: field.value,
-        required: field.required,
+        data: {
+          text: field.label,
+          initial: field.value,
+          required: field.required,
+        },
       });
     } else if (field.type === 'not-supported') {
       console.error(`Skipping field: ${field.error}`);
@@ -83,7 +91,9 @@ export const addDocumentFieldsToForm = (
   elements.push({
     id: 'root',
     type: 'sequence',
-    elements: elements.map(element => element.id),
+    data: {
+      elements: elements.map(element => element.id),
+    },
   });
   return addFormElements(form, elements, 'root');
 };
diff --git a/packages/form-service/src/operations/get-form.ts b/packages/form-service/src/operations/get-form.ts
index 573bbafa..4c5c94b4 100644
--- a/packages/form-service/src/operations/get-form.ts
+++ b/packages/form-service/src/operations/get-form.ts
@@ -1,6 +1,6 @@
 import { Result } from '@atj/common';
-
 import { type FormDefinition } from '@atj/forms';
+
 import { getFormFromStorage } from '../context/browser/form-repo';
 
 export const getForm = (
diff --git a/packages/forms/src/config/config.ts b/packages/forms/src/config/config.ts
new file mode 100644
index 00000000..d23a1d00
--- /dev/null
+++ b/packages/forms/src/config/config.ts
@@ -0,0 +1,13 @@
+import { type FormConfig } from '.';
+import { inputConfig } from './elements/input';
+import { sequenceConfig } from './elements/sequence';
+
+// This configuration reflects what a user of this library would provide for
+// their usage scenarios. For now, keep here in the form service until we
+// understand the usage scenarios better.
+export const defaultFormConfig: FormConfig = {
+  elements: {
+    input: inputConfig,
+    sequence: sequenceConfig,
+  },
+};
diff --git a/packages/forms/src/config/elements/input.ts b/packages/forms/src/config/elements/input.ts
new file mode 100644
index 00000000..b2bdd799
--- /dev/null
+++ b/packages/forms/src/config/elements/input.ts
@@ -0,0 +1,35 @@
+import { type FormElementConfig } from '..';
+import { type FormElement } from '../../elements';
+import { PromptPart } from '../../prompts';
+
+export type InputElement = FormElement<{
+  text: string;
+  initial: string;
+  required: boolean;
+}>;
+
+export const inputConfig: FormElementConfig<InputElement> = {
+  initial: {
+    text: '',
+    initial: '',
+    required: true,
+  },
+  parseData(obj: any) {
+    // TODO: runtime validation
+    return obj;
+  },
+  getChildren() {
+    return [];
+  },
+  createPrompt(_, session, element): PromptPart[] {
+    return [
+      {
+        type: 'text' as const,
+        id: element.id,
+        value: session.data.values[element.id],
+        label: element.data.text,
+        required: element.data.required,
+      },
+    ];
+  },
+};
diff --git a/packages/forms/src/config/elements/sequence.ts b/packages/forms/src/config/elements/sequence.ts
new file mode 100644
index 00000000..3629989f
--- /dev/null
+++ b/packages/forms/src/config/elements/sequence.ts
@@ -0,0 +1,27 @@
+import { type FormElementConfig } from '..';
+import { type FormElement, type FormElementId } from '../../elements';
+import { type PromptPart, createPromptForElement } from '../../prompts';
+
+export type SequenceElement = FormElement<{
+  elements: FormElementId[];
+}>;
+
+export const sequenceConfig: FormElementConfig<SequenceElement> = {
+  initial: {
+    elements: [],
+  },
+  parseData(obj) {
+    return obj;
+  },
+  getChildren(element, elements) {
+    return element.data.elements.map(
+      (elementId: string) => elements[elementId]
+    );
+  },
+  createPrompt(config, session, element): PromptPart[] {
+    return element.data.elements.flatMap((elementId: string) => {
+      const element = session.form.elements[elementId];
+      return createPromptForElement(config, session, element);
+    });
+  },
+};
diff --git a/packages/forms/src/config/index.ts b/packages/forms/src/config/index.ts
index 88415406..4d954854 100644
--- a/packages/forms/src/config/index.ts
+++ b/packages/forms/src/config/index.ts
@@ -1,15 +1,21 @@
-import { PromptPart } from '../prompts';
+import { type FormElement, type FormElementId } from '../elements';
+import { type CreatePrompt } from '../prompts';
 
-type FormConfigContext = {};
+export { defaultFormConfig } from './config';
 
-type FormElement<Schema extends { type: string }> = {
-  parseData: (data: Schema) => Schema | Error;
-  createPrompt?: (session: any, data: Schema) => PromptPart[]; // if a terminal element???
+export type FormElementConfig<ThisFormElement extends FormElement<any>> = {
+  initial: ThisFormElement['data'];
+  parseData: (obj: any) => ThisFormElement;
+  getChildren: (
+    element: ThisFormElement,
+    elements: Record<FormElementId, FormElement<any>>
+  ) => FormElement<any>[];
+  createPrompt: CreatePrompt<ThisFormElement>;
 };
-
-export const createFormConfig = (context: FormConfigContext) => {
-  // or should this be createFormManager?
+export type FormConfig<T extends FormElement<any> = FormElement<any>> = {
+  elements: Record<string, FormElementConfig<T>>;
 };
 
-// don't design this backwards. might want to start at createPrompt, and add
-// in the bits that are actually required.
+export type ConfigElements<Config extends FormConfig> = ReturnType<
+  Config['elements'][keyof Config['elements']]['parseData']
+>;
diff --git a/packages/forms/src/elements.ts b/packages/forms/src/elements.ts
new file mode 100644
index 00000000..1b183262
--- /dev/null
+++ b/packages/forms/src/elements.ts
@@ -0,0 +1,24 @@
+import { FormDefinition } from '..';
+
+export type FormElement<T = any> = {
+  type: string;
+  id: FormElementId;
+  data: T;
+};
+
+export type FormElementId = string;
+export type FormElementValue = any;
+export type FormElementValueMap = Record<FormElementId, FormElementValue>;
+export type FormElementMap = Record<FormElementId, FormElement<any>>;
+export type GetFormElement = (
+  form: FormDefinition,
+  id: FormElementId
+) => FormElement<any>;
+
+export const getFormElementMap = (elements: FormElement<any>[]) => {
+  return Object.fromEntries(
+    elements.map(element => {
+      return [element.id, element];
+    })
+  );
+};
diff --git a/packages/forms/src/elements/index.ts b/packages/forms/src/elements/index.ts
deleted file mode 100644
index 905750d8..00000000
--- a/packages/forms/src/elements/index.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { InputElement } from './input';
-import { SequenceElement } from './sequence';
-
-// The collection of all form elements is a discriminated union.
-// We may want the user of this library to be able to inject their own element
-// types, but for now, we will just hardcode this type.
-export type FormElement = InputElement | SequenceElement;
-
-export type FormElementId = string;
-export type FormElementValue = any;
-export type FormElementValueMap = Record<FormElementId, FormElementValue>;
-export type FormElementMap = Record<FormElementId, FormElement>;
-
-export const getFormElementMap = (elements: FormElement[]) => {
-  return Object.fromEntries(
-    elements.map(element => {
-      return [element.id, element];
-    })
-  );
-};
diff --git a/packages/forms/src/elements/input.ts b/packages/forms/src/elements/input.ts
deleted file mode 100644
index 0b05c113..00000000
--- a/packages/forms/src/elements/input.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { FormElementId } from '.';
-
-export type InputElement = {
-  type: 'input';
-  id: FormElementId;
-  text: string;
-  initial: string | boolean | string[]; // TODO: create separate types
-  required: boolean;
-};
diff --git a/packages/forms/src/elements/sequence.ts b/packages/forms/src/elements/sequence.ts
deleted file mode 100644
index cd963c02..00000000
--- a/packages/forms/src/elements/sequence.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { FormElementId } from '.';
-
-export type SequenceElement = {
-  id: FormElementId;
-  type: 'sequence';
-  elements: FormElementId[];
-};
diff --git a/packages/forms/src/index.ts b/packages/forms/src/index.ts
index f0e87bcd..7686746c 100644
--- a/packages/forms/src/index.ts
+++ b/packages/forms/src/index.ts
@@ -1,13 +1,16 @@
+import { FormConfig } from './config';
+import { SequenceElement } from './config/elements/sequence';
 import { type DocumentFieldMap } from './documents';
 import {
-  getFormElementMap,
-  type FormElement,
   type FormElementId,
+  type FormElementMap,
   type FormElementValue,
   type FormElementValueMap,
-  type FormElementMap,
+  getFormElementMap,
+  FormElement,
 } from './elements';
 
+export * from './config';
 export * from './documents';
 export * from './elements';
 export * from './prompts';
@@ -43,32 +46,25 @@ type FormOutput = {
 export const createForm = (
   summary: FormSummary,
   initial: {
-    elements: FormElement[];
+    elements: FormElement<any>[];
     root: FormElementId;
   } = {
     elements: [
       {
         id: 'root',
         type: 'sequence',
-        elements: [],
-      },
+        data: {
+          elements: [],
+        },
+      } satisfies SequenceElement,
     ],
     root: 'root',
   }
 ): FormDefinition => {
   return {
     summary,
-    root: 'root',
-    elements: {
-      root: {
-        id: 'root',
-        type: 'sequence',
-        elements: initial.elements.map(element => {
-          return element.id;
-        }),
-      },
-      ...getFormElementMap(initial.elements),
-    },
+    root: initial.root,
+    elements: getFormElementMap(initial.elements),
     outputs: [],
   };
 };
@@ -77,24 +73,13 @@ export const getRootFormElement = (form: FormDefinition) => {
   return form.elements[form.root];
 };
 
-const initialValueForFormElement = (element: FormElement) => {
-  if (element.type === 'input') {
-    return element.initial;
-  } else if (element.type === 'sequence') {
-    return [];
-  } else {
-    const _exhaustiveCheck: never = element;
-    return _exhaustiveCheck;
-  }
-};
-
 export const createFormSession = (form: FormDefinition): FormSession => {
   return {
     data: {
       errors: {},
       values: Object.fromEntries(
         Object.values(form.elements).map(element => {
-          return [element.id, initialValueForFormElement(element)];
+          return [element.id, form.elements[element.id].data.initial];
         })
       ),
     },
@@ -114,7 +99,7 @@ export const updateForm = (
   const nextForm = addValue(context, id, value);
   const element = context.form.elements[id];
   if (element.type === 'input') {
-    if (element.required && !value) {
+    if (element.data.required && !value) {
       return addError(nextForm, id, 'Required value not provided.');
     }
   }
@@ -153,7 +138,7 @@ const addError = (
 
 export const addFormElements = (
   form: FormDefinition,
-  elements: FormElement[],
+  elements: FormElement<any>[],
   root?: FormElementId
 ) => {
   const formElementMap = getFormElementMap(elements);
@@ -166,7 +151,7 @@ export const addFormElements = (
 
 export const replaceFormElements = (
   form: FormDefinition,
-  elements: FormElement[]
+  elements: FormElement<any>[]
 ): FormDefinition => {
   return {
     ...form,
@@ -175,12 +160,13 @@ export const replaceFormElements = (
         acc[element.id] = element;
         return acc;
       },
-      {} as Record<FormElementId, FormElement>
+      {} as Record<FormElementId, FormElement<any>>
     ),
   };
 };
 
 export const updateElements = (
+  config: FormConfig,
   form: FormDefinition,
   newElements: FormElementMap
 ): FormDefinition => {
@@ -188,35 +174,17 @@ export const updateElements = (
   const targetElements: FormElementMap = {
     root,
   };
-  contributeElements(targetElements, newElements, root);
+  const resource = config.elements[root.type as keyof FormConfig];
+  const children = resource.getChildren(root, newElements);
+  targetElements[root.id] = root;
+  children.forEach(child => (targetElements[child.id] = child));
+
   return {
     ...form,
     elements: targetElements,
   };
 };
 
-// Contribute a FormElement and all its children to a FormElementMap.
-// This function may be used to create a minimal map of required fields.
-const contributeElements = (
-  target: FormElementMap,
-  source: FormElementMap,
-  element: FormElement
-): FormElementMap => {
-  if (element.type === 'input') {
-    target[element.id] = element;
-    return target;
-  } else if (element.type === 'sequence') {
-    element.elements.forEach(elementId => {
-      const sequenceElement = source[elementId];
-      return contributeElements(target, source, sequenceElement);
-    });
-    return target;
-  } else {
-    const _exhaustiveCheck: never = element;
-    return _exhaustiveCheck;
-  }
-};
-
 export const addFormOutput = (form: FormDefinition, document: FormOutput) => {
   return {
     ...form,
diff --git a/packages/forms/src/prompts/index.ts b/packages/forms/src/prompts.ts
similarity index 55%
rename from packages/forms/src/prompts/index.ts
rename to packages/forms/src/prompts.ts
index 1404eb9a..635eb770 100644
--- a/packages/forms/src/prompts/index.ts
+++ b/packages/forms/src/prompts.ts
@@ -1,6 +1,11 @@
 // For now, a prompt just returns an array of elements. This will likely need
 
-import { getRootFormElement, type FormSession, type FormElement } from '..';
+import {
+  getRootFormElement,
+  type FormSession,
+  type FormElement,
+  FormConfig,
+} from '..';
 
 export type TextInputPrompt = {
   type: 'text';
@@ -30,7 +35,10 @@ export type Prompt = {
 };
 
 // to be filled out to support more complicated display formats.
-export const createPrompt = (session: FormSession): Prompt => {
+export const createPrompt = (
+  config: FormConfig,
+  session: FormSession
+): Prompt => {
   const parts: PromptPart[] = [
     {
       type: 'form-summary',
@@ -39,7 +47,7 @@ export const createPrompt = (session: FormSession): Prompt => {
     },
   ];
   const root = getRootFormElement(session.form);
-  parts.push(...createPromptForElement(session, root));
+  parts.push(...createPromptForElement(config, session, root));
   return {
     actions: [
       {
@@ -51,27 +59,16 @@ export const createPrompt = (session: FormSession): Prompt => {
   };
 };
 
-const createPromptForElement = (
+export type CreatePrompt<T> = (
+  config: FormConfig,
   session: FormSession,
-  element: FormElement
-): PromptPart[] => {
-  if (element.type === 'input') {
-    return [
-      {
-        type: 'text' as const,
-        id: element.id,
-        value: session.data.values[element.id],
-        label: element.text,
-        required: element.required,
-      },
-    ];
-  } else if (element.type === 'sequence') {
-    return element.elements.flatMap(elementId => {
-      const element = session.form.elements[elementId];
-      return createPromptForElement(session, element);
-    });
-  } else {
-    const _exhaustiveCheck: never = element;
-    return _exhaustiveCheck;
-  }
+  element: T
+) => PromptPart[];
+
+export const createPromptForElement: CreatePrompt<FormElement<any>> = (
+  config,
+  session,
+  element
+) => {
+  return config.elements[element.type].createPrompt(config, session, element);
 };
diff --git a/packages/forms/tests/two-field-form.test.ts b/packages/forms/tests/two-field-form.test.ts
index 189928af..259144ff 100644
--- a/packages/forms/tests/two-field-form.test.ts
+++ b/packages/forms/tests/two-field-form.test.ts
@@ -3,25 +3,31 @@ import { describe, expect, test } from 'vitest';
 import * as forms from '../src';
 import { createPrompt } from '../src';
 
-const elements: forms.FormElement[] = [
+const elements: forms.FormElement<any>[] = [
   {
     type: 'sequence',
     id: 'root',
-    elements: ['element-1', 'element-2'],
+    data: {
+      elements: ['element-1', 'element-2'],
+    },
   },
   {
     type: 'input',
     id: 'element-1',
-    text: 'What is your first name?',
-    initial: '',
-    required: true,
+    data: {
+      text: 'What is your first name?',
+      initial: '',
+      required: true,
+    },
   },
   {
     type: 'input',
     id: 'element-2',
-    text: 'What is your favorite word?',
-    initial: '',
-    required: false,
+    data: {
+      text: 'What is your favorite word?',
+      initial: '',
+      required: false,
+    },
   },
 ];
 const form = forms.createForm(
@@ -123,9 +129,10 @@ describe('two element form session', () => {
 });
 
 describe('two element prompt', () => {
+  const config = forms.defaultFormConfig;
   const session = forms.createFormSession(form);
   test('includes a submit button', () => {
-    const prompt = createPrompt(session);
+    const prompt = createPrompt(config, session);
     expect(prompt.actions).toEqual([
       {
         type: 'submit',
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 8e99f585..f8d40647 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -15,6 +15,9 @@ importers:
       '@types/node':
         specifier: ^20.11.16
         version: 20.11.16
+      '@vitest/coverage-c8':
+        specifier: ^0.33.0
+        version: 0.33.0(vitest@0.34.6)
       '@vitest/coverage-v8':
         specifier: ^1.2.2
         version: 1.2.2(vitest@0.34.6)
@@ -3246,18 +3249,18 @@ packages:
       exit: 0.1.2
       glob: 7.2.3
       graceful-fs: 4.2.11
-      istanbul-lib-coverage: 3.2.0
+      istanbul-lib-coverage: 3.2.2
       istanbul-lib-instrument: 6.0.1
       istanbul-lib-report: 3.0.1
       istanbul-lib-source-maps: 4.0.1
-      istanbul-reports: 3.1.5
+      istanbul-reports: 3.1.6
       jest-message-util: 29.7.0
       jest-util: 29.7.0
       jest-worker: 29.7.0
       slash: 3.0.0
       string-length: 4.0.2
       strip-ansi: 6.0.1
-      v8-to-istanbul: 9.1.0
+      v8-to-istanbul: 9.2.0
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -4924,7 +4927,7 @@ packages:
       express: 4.18.2
       find-cache-dir: 3.3.2
       fs-extra: 11.1.1
-      magic-string: 0.30.3
+      magic-string: 0.30.7
       rollup: 3.29.1
       typescript: 5.3.3
       vite: 5.0.12(@types/node@20.11.16)
@@ -6230,6 +6233,20 @@ packages:
     transitivePeerDependencies:
       - supports-color
 
+  /@vitest/coverage-c8@0.33.0(vitest@0.34.6):
+    resolution: {integrity: sha512-DaF1zJz4dcOZS4k/neiQJokmOWqsGXwhthfmUdPGorXIQHjdPvV6JQSYhQDI41MyI8c+IieQUdIDs5XAMHtDDw==}
+    deprecated: v8 coverage is moved to @vitest/coverage-v8 package
+    peerDependencies:
+      vitest: '>=0.30.0 <1'
+    dependencies:
+      '@ampproject/remapping': 2.2.1
+      c8: 7.14.0
+      magic-string: 0.30.7
+      picocolors: 1.0.0
+      std-env: 3.7.0
+      vitest: 0.34.6(@vitest/ui@1.2.2)
+    dev: true
+
   /@vitest/coverage-v8@0.34.6(vitest@0.34.6):
     resolution: {integrity: sha512-fivy/OK2d/EsJFoEoxHFEnNGTg+MmdZBAVK9Ka4qhXR2K3J0DS08vcGVwzDtXSuUMabLv4KtPcpSKkcMXFDViw==}
     peerDependencies:
@@ -7608,6 +7625,25 @@ packages:
     resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
     engines: {node: '>= 0.8'}
 
+  /c8@7.14.0:
+    resolution: {integrity: sha512-i04rtkkcNcCf7zsQcSv/T9EbUn4RXQ6mropeMcjFOsQXQ0iGLAr/xT6TImQg4+U9hmNpN9XdvPkjUL1IzbgxJw==}
+    engines: {node: '>=10.12.0'}
+    hasBin: true
+    dependencies:
+      '@bcoe/v8-coverage': 0.2.3
+      '@istanbuljs/schema': 0.1.3
+      find-up: 5.0.0
+      foreground-child: 2.0.0
+      istanbul-lib-coverage: 3.2.2
+      istanbul-lib-report: 3.0.1
+      istanbul-reports: 3.1.6
+      rimraf: 3.0.2
+      test-exclude: 6.0.0
+      v8-to-istanbul: 9.2.0
+      yargs: 16.2.0
+      yargs-parser: 20.2.9
+    dev: true
+
   /cac@6.7.14:
     resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
     engines: {node: '>=8'}
@@ -8030,7 +8066,6 @@ packages:
       string-width: 4.2.3
       strip-ansi: 6.0.1
       wrap-ansi: 7.0.0
-    dev: false
 
   /cliui@8.0.1:
     resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
@@ -8984,7 +9019,7 @@ packages:
     dependencies:
       semver: 7.5.4
       shelljs: 0.8.5
-      typescript: 5.4.0-dev.20240206
+      typescript: 5.4.0-dev.20240209
     dev: false
 
   /dset@3.1.3:
@@ -11736,7 +11771,7 @@ packages:
     dependencies:
       '@babel/core': 7.23.7
       '@istanbuljs/schema': 0.1.3
-      istanbul-lib-coverage: 3.2.0
+      istanbul-lib-coverage: 3.2.2
       semver: 6.3.1
     transitivePeerDependencies:
       - supports-color
@@ -11749,7 +11784,7 @@ packages:
       '@babel/core': 7.23.7
       '@babel/parser': 7.23.6
       '@istanbuljs/schema': 0.1.3
-      istanbul-lib-coverage: 3.2.0
+      istanbul-lib-coverage: 3.2.2
       semver: 6.3.1
     transitivePeerDependencies:
       - supports-color
@@ -11774,7 +11809,7 @@ packages:
     dependencies:
       archy: 1.0.0
       cross-spawn: 7.0.3
-      istanbul-lib-coverage: 3.2.0
+      istanbul-lib-coverage: 3.2.2
       p-map: 3.0.0
       rimraf: 3.0.2
       uuid: 8.3.2
@@ -14185,13 +14220,13 @@ packages:
       foreground-child: 2.0.0
       get-package-type: 0.1.0
       glob: 7.2.3
-      istanbul-lib-coverage: 3.2.0
+      istanbul-lib-coverage: 3.2.2
       istanbul-lib-hook: 3.0.0
       istanbul-lib-instrument: 4.0.3
       istanbul-lib-processinfo: 2.0.3
       istanbul-lib-report: 3.0.1
       istanbul-lib-source-maps: 4.0.1
-      istanbul-reports: 3.1.5
+      istanbul-reports: 3.1.6
       make-dir: 3.1.0
       node-preload: 0.2.1
       p-map: 3.0.0
@@ -17752,8 +17787,8 @@ packages:
     engines: {node: '>=14.17'}
     hasBin: true
 
-  /typescript@5.4.0-dev.20240206:
-    resolution: {integrity: sha512-8P1XYxDbG/AyGE5tB8+JpeiQfS5ye1BTvIVDZaHhoK9nJuCn4nkB0L66lvfwYB+46hA4rLo3vE3WkIToSYtqQA==}
+  /typescript@5.4.0-dev.20240209:
+    resolution: {integrity: sha512-qhstJB8bOkf9hDabhxluOD3J94iUqsQZtTcia/T8ymwqh2ziTXx9z9OQg3sxJOcUEVVxwAxKybCiwLM2HxwPwg==}
     engines: {node: '>=14.17'}
     hasBin: true
     dev: false
@@ -19088,7 +19123,6 @@ packages:
   /yargs-parser@20.2.9:
     resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
     engines: {node: '>=10'}
-    dev: false
 
   /yargs-parser@21.1.1:
     resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
@@ -19128,7 +19162,6 @@ packages:
       string-width: 4.2.3
       y18n: 5.0.8
       yargs-parser: 20.2.9
-    dev: false
 
   /yargs@17.7.2:
     resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}