Repository: apache/airflow
Pull request #49763: Add `dag_run_conf` to `RunBackfillForm`
Author: login: guan404ming
Labels: name: area:UI
description: Related to UI/UX. For Frontend Developers.
Created: 2025-04-25T06:29:53Z | Merged: 2025-04-30T17:49:30Z

<!--
 Licensed to the Apache Software Foundation (ASF) under one
 or more contributor license agreements.  See the NOTICE file
 distributed with this work for additional information
 regarding copyright ownership.  The ASF licenses this file
 to you under the Apache License, Version 2.0 (the
 "License"); you may not use this file except in compliance
 with the License.  You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing,
 software distributed under the License is distributed on an
 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 -->

<!--
Thank you for contributing! Please make sure that your code changes
are covered with tests. And in case of new features or big changes
remember to adjust the documentation.

Feel free to ping committers for the review!

In case of an existing issue, reference it using one of the following:

closes: #ISSUE
related: #ISSUE

How to write a good git commit message:
http://chris.beams.io/posts/git-commit/
-->

## Related Issue

closes #49653
cc: @bbovenzi

## Why 

- currently the RunBackfillForm has no place to input the `dag_run_conf` however it's needed

## How

- add `JsonEditor` in a `Accordion` to let user input `dag_run_conf`:

![image](https://github.com/user-attachments/assets/3106a793-ffb4-451a-9f8e-6b0ccb2f03fc)


<!-- Please keep an empty line above the dashes. -->
---
**^ Add meaningful description above**
Read the **[Pull Request Guidelines](https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#pull-request-guidelines)** for more information.
In case of fundamental code changes, an Airflow Improvement Proposal ([AIP](https://cwiki.apache.org/confluence/display/AIRFLOW/Airflow+Improvement+Proposals)) is needed.
In case of a new dependency, check compliance with the [ASF 3rd Party License Policy](https://www.apache.org/legal/resolved.html#category-x).
In case of backwards incompatible changes please leave a note in a newsfragment file, named `{pr_number}.significant.rst` or `{issue_number}.significant.rst`, in [airflow-core/newsfragments](https://github.com/apache/airflow/tree/main/airflow-core/newsfragments).


Linked issue #49653: Add Dag Run Conf to Run Backfill form
### Body

Creating a new Backfill is triggering many dag runs, so in our combined Trigger Run/Run Backfill modal we should keep the dag run conf available for both options and pass the conf value to the create backfill API request.

### Committer

- [x] I acknowledge that I am a maintainer/committer of the Apache Airflow project.
Linked issue comments: user: login: guan404ming
created_at: 2025-04-24T16:52:46Z
body: Hi, I think I could help with this~
author_association: MEMBER
user: login: bbovenzi
created_at: 2025-04-24T16:58:46Z
body: Go for it!

author_association: CONTRIBUTOR

Diff:
diff --git a/airflow-core/src/airflow/ui/src/components/ConfigForm.tsx b/airflow-core/src/airflow/ui/src/components/ConfigForm.tsx
new file mode 100644
index 0000000000000..a216b22de60c6
--- /dev/null
+++ b/airflow-core/src/airflow/ui/src/components/ConfigForm.tsx
@@ -0,0 +1,118 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { Accordion, Box, Field } from "@chakra-ui/react";
+import { type Control, type FieldValues, type Path, Controller } from "react-hook-form";
+
+import type { ParamsSpec } from "src/queries/useDagParams";
+import { useParamStore } from "src/queries/useParamStore";
+
+import { FlexibleForm, flexibleFormDefaultSection } from "./FlexibleForm";
+import { JsonEditor } from "./JsonEditor";
+
+type ConfigFormProps<T extends FieldValues = FieldValues> = {
+  readonly children?: React.ReactNode;
+  readonly control: Control<T>;
+  readonly errors: {
+    conf?: string;
+    date?: unknown;
+  };
+  readonly initialParamsDict: { paramsDict: ParamsSpec };
+  readonly setErrors: React.Dispatch<
+    React.SetStateAction<{
+      conf?: string;
+      date?: unknown;
+    }>
+  >;
+};
+
+const ConfigForm = <T extends FieldValues = FieldValues>({
+  children,
+  control,
+  errors,
+  initialParamsDict,
+  setErrors,
+}: ConfigFormProps<T>) => {
+  const { conf, setConf } = useParamStore();
+
+  const validateAndPrettifyJson = (value: string) => {
+    try {
+      const parsedJson = JSON.parse(value) as JSON;
+
+      setErrors((prev) => ({ ...prev, conf: undefined }));
+
+      const formattedJson = JSON.stringify(parsedJson, undefined, 2);
+
+      if (formattedJson !== conf) {
+        setConf(formattedJson); // Update only if the value is different
+      }
+
+      return formattedJson;
+    } catch (error) {
+      const errorMessage = error instanceof Error ? error.message : "Unknown error occurred.";
+
+      setErrors((prev) => ({
+        ...prev,
+        conf: `Invalid JSON format: ${errorMessage}`,
+      }));
+
+      return value;
+    }
+  };
+
+  return (
+    <Accordion.Root
+      collapsible
+      defaultValue={[flexibleFormDefaultSection]}
+      mb={4}
+      size="lg"
+      variant="enclosed"
+    >
+      <FlexibleForm
+        flexibleFormDefaultSection={flexibleFormDefaultSection}
+        initialParamsDict={initialParamsDict}
+      />
+      <Accordion.Item key="advancedOptions" value="advancedOptions">
+        <Accordion.ItemTrigger cursor="button">Advanced Options</Accordion.ItemTrigger>
+        <Accordion.ItemContent>
+          <Box p={4}>
+            {children}
+            <Controller
+              control={control}
+              name={"conf" as Path<T>}
+              render={({ field }) => (
+                <Field.Root invalid={Boolean(errors.conf)} mt={6}>
+                  <Field.Label fontSize="md">Configuration JSON</Field.Label>
+                  <JsonEditor
+                    {...field}
+                    onBlur={() => {
+                      field.onChange(validateAndPrettifyJson(field.value as string));
+                    }}
+                  />
+                  {Boolean(errors.conf) ? <Field.ErrorText>{errors.conf}</Field.ErrorText> : undefined}
+                </Field.Root>
+              )}
+            />
+          </Box>
+        </Accordion.ItemContent>
+      </Accordion.Item>
+    </Accordion.Root>
+  );
+};
+
+export default ConfigForm;
diff --git a/airflow-core/src/airflow/ui/src/components/DagActions/RunBackfillForm.tsx b/airflow-core/src/airflow/ui/src/components/DagActions/RunBackfillForm.tsx
index 4ab35b2f466d2..2079de6c60479 100644
--- a/airflow-core/src/airflow/ui/src/components/DagActions/RunBackfillForm.tsx
+++ b/airflow-core/src/airflow/ui/src/components/DagActions/RunBackfillForm.tsx
@@ -25,11 +25,15 @@ import { Button } from "src/components/ui";
 import { reprocessBehaviors } from "src/constants/reprocessBehaviourParams";
 import { useCreateBackfill } from "src/queries/useCreateBackfill";
 import { useCreateBackfillDryRun } from "src/queries/useCreateBackfillDryRun";
+import { useDagParams } from "src/queries/useDagParams";
+import { useParamStore } from "src/queries/useParamStore";
 import { useTogglePause } from "src/queries/useTogglePause";
 import { pluralize } from "src/utils";
 
+import ConfigForm from "../ConfigForm";
 import { DateTimeInput } from "../DateTimeInput";
 import { ErrorAlert } from "../ErrorAlert";
+import type { DagRunTriggerParams } from "../TriggerDag/TriggerDAGForm";
 import { Checkbox } from "../ui/Checkbox";
 import { RadioCardItem, RadioCardLabel, RadioCardRoot } from "../ui/RadioCard";
 
@@ -39,14 +43,17 @@ type RunBackfillFormProps = {
 };
 const today = new Date().toISOString().slice(0, 16);
 
+type BackfillFormProps = DagRunTriggerParams & Omit<BackfillPostBody, "dag_run_conf">;
+
 const RunBackfillForm = ({ dag, onClose }: RunBackfillFormProps) => {
   const [errors, setErrors] = useState<{ conf?: string; date?: unknown }>({});
   const [unpause, setUnpause] = useState(true);
-
-  const { control, handleSubmit, reset, watch } = useForm<BackfillPostBody>({
+  const initialParamsDict = useDagParams(dag.dag_id, true);
+  const { conf } = useParamStore();
+  const { control, handleSubmit, reset, watch } = useForm<BackfillFormProps>({
     defaultValues: {
+      conf,
       dag_id: dag.dag_id,
-      dag_run_conf: {},
       from_date: "",
       max_active_runs: 1,
       reprocess_behavior: "none",
@@ -55,7 +62,7 @@ const RunBackfillForm = ({ dag, onClose }: RunBackfillFormProps) => {
     },
     mode: "onBlur",
   });
-  const values = useWatch<BackfillPostBody>({
+  const values = useWatch<BackfillFormProps>({
     control,
   });
 
@@ -85,10 +92,16 @@ const RunBackfillForm = ({ dag, onClose }: RunBackfillFormProps) => {
     }
   }, [dateValidationError]);
 
+  useEffect(() => {
+    if (conf) {
+      reset({ conf });
+    }
+  }, [conf, reset]);
+
   const dataIntervalStart = watch("from_date");
   const dataIntervalEnd = watch("to_date");
 
-  const onSubmit = (fdata: BackfillPostBody) => {
+  const onSubmit = (fdata: BackfillFormProps) => {
     if (unpause && dag.is_paused) {
       togglePause({
         dagId: dag.dag_id,
@@ -98,11 +111,14 @@ const RunBackfillForm = ({ dag, onClose }: RunBackfillFormProps) => {
       });
     }
     createBackfill({
-      requestBody: fdata,
+      requestBody: {
+        ...fdata,
+        dag_run_conf: JSON.parse(fdata.conf) as Record<string, unknown>,
+      },
     });
   };
 
-  const onCancel = (fdata: BackfillPostBody) => {
+  const onCancel = (fdata: BackfillFormProps) => {
     reset(fdata);
     onClose();
   };
@@ -237,6 +253,13 @@ const RunBackfillForm = ({ dag, onClose }: RunBackfillFormProps) => {
             <Spacer />
           </>
         ) : undefined}
+
+        <ConfigForm
+          control={control}
+          errors={errors}
+          initialParamsDict={initialParamsDict}
+          setErrors={setErrors}
+        />
       </VStack>
       <Box as="footer" display="flex" justifyContent="flex-end" mt={4}>
         <HStack w="full">
diff --git a/airflow-core/src/airflow/ui/src/components/TriggerDag/TriggerDAGForm.tsx b/airflow-core/src/airflow/ui/src/components/TriggerDag/TriggerDAGForm.tsx
index b3d470ba4170e..fdeae4bdb6958 100644
--- a/airflow-core/src/airflow/ui/src/components/TriggerDag/TriggerDAGForm.tsx
+++ b/airflow-core/src/airflow/ui/src/components/TriggerDag/TriggerDAGForm.tsx
@@ -16,10 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { Input, Button, Box, Spacer, HStack, Field, Stack } from "@chakra-ui/react";
+import { Button, Box, Spacer, HStack, Input, Field, Stack } from "@chakra-ui/react";
 import dayjs from "dayjs";
 import { useEffect, useState } from "react";
-import { useForm, Controller } from "react-hook-form";
+import { Controller, useForm } from "react-hook-form";
 import { FiPlay } from "react-icons/fi";
 
 import { useDagParams } from "src/queries/useDagParams";
@@ -27,11 +27,9 @@ import { useParamStore } from "src/queries/useParamStore";
 import { useTogglePause } from "src/queries/useTogglePause";
 import { useTrigger } from "src/queries/useTrigger";
 
+import ConfigForm from "../ConfigForm";
 import { DateTimeInput } from "../DateTimeInput";
 import { ErrorAlert } from "../ErrorAlert";
-import { FlexibleForm, flexibleFormDefaultSection } from "../FlexibleForm";
-import { JsonEditor } from "../JsonEditor";
-import { Accordion } from "../ui";
 import { Checkbox } from "../ui/Checkbox";
 import EditableMarkdown from "./EditableMarkdown";
 
@@ -53,7 +51,7 @@ const TriggerDAGForm = ({ dagId, isPaused, onClose, open }: TriggerDAGFormProps)
   const [errors, setErrors] = useState<{ conf?: string; date?: unknown }>({});
   const initialParamsDict = useDagParams(dagId, open);
   const { error: errorTrigger, isPending, triggerDagRun } = useTrigger({ dagId, onSuccessConfirm: onClose });
-  const { conf, setConf } = useParamStore();
+  const { conf } = useParamStore();
   const [unpause, setUnpause] = useState(true);
 
   const { mutate: togglePause } = useTogglePause({ dagId });
@@ -75,6 +73,10 @@ const TriggerDAGForm = ({ dagId, isPaused, onClose, open }: TriggerDAGFormProps)
     }
   }, [conf, reset]);
 
+  const resetDateError = () => {
+    setErrors((prev) => ({ ...prev, date: undefined }));
+  };
+
   const onSubmit = (data: DagRunTriggerParams) => {
     if (unpause && isPaused) {
       togglePause({
@@ -87,119 +89,59 @@ const TriggerDAGForm = ({ dagId, isPaused, onClose, open }: TriggerDAGFormProps)
     triggerDagRun(data);
   };
 
-  const validateAndPrettifyJson = (value: string) => {
-    try {
-      const parsedJson = JSON.parse(value) as JSON;
-
-      setErrors((prev) => ({ ...prev, conf: undefined }));
-
-      const formattedJson = JSON.stringify(parsedJson, undefined, 2);
-
-      if (formattedJson !== conf) {
-        setConf(formattedJson); // Update only if the value is different
-      }
-
-      return formattedJson;
-    } catch (error) {
-      const errorMessage = error instanceof Error ? error.message : "Unknown error occurred.";
-
-      setErrors((prev) => ({
-        ...prev,
-        conf: `Invalid JSON format: ${errorMessage}`,
-      }));
-
-      return value;
-    }
-  };
-
-  const resetDateError = () => {
-    setErrors((prev) => ({ ...prev, date: undefined }));
-  };
-
   return (
-    <>
-      <Accordion.Root
-        collapsible
-        defaultValue={[flexibleFormDefaultSection]}
-        mb={4}
-        mt={8}
-        size="lg"
-        variant="enclosed"
+    <Box mt={8}>
+      <ConfigForm
+        control={control}
+        errors={errors}
+        initialParamsDict={initialParamsDict}
+        setErrors={setErrors}
       >
-        <FlexibleForm
-          flexibleFormDefaultSection={flexibleFormDefaultSection}
-          initialParamsDict={initialParamsDict}
+        <Controller
+          control={control}
+          name="logicalDate"
+          render={({ field }) => (
+            <Field.Root invalid={Boolean(errors.date)} orientation="horizontal">
+              <Stack>
+                <Field.Label fontSize="md" style={{ flexBasis: "30%" }}>
+                  Logical Date
+                </Field.Label>
+              </Stack>
+              <Stack css={{ flexBasis: "70%" }}>
+                <DateTimeInput {...field} onBlur={resetDateError} size="sm" />
+              </Stack>
+            </Field.Root>
+          )}
         />
-        <Accordion.Item key="advancedOptions" value="advancedOptions">
-          <Accordion.ItemTrigger cursor="button">Advanced Options</Accordion.ItemTrigger>
-          <Accordion.ItemContent>
-            <Box p={4}>
-              <Controller
-                control={control}
-                name="logicalDate"
-                render={({ field }) => (
-                  <Field.Root invalid={Boolean(errors.date)} orientation="horizontal">
-                    <Stack>
-                      <Field.Label fontSize="md" style={{ flexBasis: "30%" }}>
-                        Logical Date
-                      </Field.Label>
-                    </Stack>
-                    <Stack css={{ flexBasis: "70%" }}>
-                      <DateTimeInput {...field} onBlur={resetDateError} size="sm" />
-                    </Stack>
-                  </Field.Root>
-                )}
-              />
-
-              <Controller
-                control={control}
-                name="dagRunId"
-                render={({ field }) => (
-                  <Field.Root mt={6} orientation="horizontal">
-                    <Stack>
-                      <Field.Label fontSize="md" style={{ flexBasis: "30%" }}>
-                        Run ID
-                      </Field.Label>
-                    </Stack>
-                    <Stack css={{ flexBasis: "70%" }}>
-                      <Input {...field} size="sm" />
-                      <Field.HelperText>Optional - will be generated if not provided</Field.HelperText>
-                    </Stack>
-                  </Field.Root>
-                )}
-              />
-
-              <Controller
-                control={control}
-                name="conf"
-                render={({ field }) => (
-                  <Field.Root invalid={Boolean(errors.conf)} mt={6}>
-                    <Field.Label fontSize="md">Configuration JSON</Field.Label>
-                    <JsonEditor
-                      {...field}
-                      onBlur={() => {
-                        field.onChange(validateAndPrettifyJson(field.value));
-                      }}
-                    />
-                    {Boolean(errors.conf) ? <Field.ErrorText>{errors.conf}</Field.ErrorText> : undefined}
-                  </Field.Root>
-                )}
-              />
 
-              <Controller
-                control={control}
-                name="note"
-                render={({ field }) => (
-                  <Field.Root mt={6}>
-                    <Field.Label fontSize="md">Dag Run Notes</Field.Label>
-                    <EditableMarkdown field={field} placeholder="Click to add note" />
-                  </Field.Root>
-                )}
-              />
-            </Box>
-          </Accordion.ItemContent>
-        </Accordion.Item>
-      </Accordion.Root>
+        <Controller
+          control={control}
+          name="dagRunId"
+          render={({ field }) => (
+            <Field.Root mt={6} orientation="horizontal">
+              <Stack>
+                <Field.Label fontSize="md" style={{ flexBasis: "30%" }}>
+                  Run ID
+                </Field.Label>
+              </Stack>
+              <Stack css={{ flexBasis: "70%" }}>
+                <Input {...field} size="sm" />
+                <Field.HelperText>Optional - will be generated if not provided</Field.HelperText>
+              </Stack>
+            </Field.Root>
+          )}
+        />
+        <Controller
+          control={control}
+          name="note"
+          render={({ field }) => (
+            <Field.Root mt={6}>
+              <Field.Label fontSize="md">Dag Run Notes</Field.Label>
+              <EditableMarkdown field={field} placeholder="Click to add note" />
+            </Field.Root>
+          )}
+        />
+      </ConfigForm>
       {isPaused ? (
         <Checkbox checked={unpause} colorPalette="blue" onChange={() => setUnpause(!unpause)}>
           Unpause {dagId} on trigger
@@ -218,7 +160,7 @@ const TriggerDAGForm = ({ dagId, isPaused, onClose, open }: TriggerDAGFormProps)
           </Button>
         </HStack>
       </Box>
-    </>
+    </Box>
   );
 };
 


Reviews:
user: login: bbovenzi
body: So for advanced options on a backfill, we only ever want the dag_run_conf. No notes, no dag run id and no logical date.

<img width="882" alt="Screenshot 2025-04-28 at 11 33 13 AM" src="https://github.com/user-attachments/assets/984699cc-0010-4c59-bdfd-591350f02a75" />

state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T15:34:24Z
user: login: bbovenzi
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T14:35:10Z
user: login: guan404ming
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-30T15:43:45Z
user: login: guan404ming
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-30T15:52:06Z
user: login: bbovenzi
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T17:48:48Z
user: login: bbovenzi
body: great work! 
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T17:49:21Z

Review comments:
diff_hunk: @@ -97,11 +106,19 @@ const RunBackfillForm = ({ dag, onClose }: RunBackfillFormProps) => {
       });
     }
     createBackfill({
-      requestBody: fdata,
+      requestBody: {
+        ...fdata,
+        dag_run_conf: {
+          conf: fdata.conf,
+          dagRunId: fdata.dagRunId,
+          logicalDate: fdata.logicalDate,
+          note: fdata.note,
+        },
path: airflow-core/src/airflow/ui/src/components/DagActions/RunBackfillForm.tsx
user: login: bbovenzi
body: ```suggestion
        dag_run_conf: fdata.conf,
```
created_at: 2025-04-28T15:33:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -39,14 +42,16 @@ type RunBackfillFormProps = {
 };
 const today = new Date().toISOString().slice(0, 16);
 
+type BackfillFormProps = DagRunTriggerParams & Omit<BackfillPostBody, "dag_run_conf">;
+
 const RunBackfillForm = ({ dag, onClose }: RunBackfillFormProps) => {
   const [errors, setErrors] = useState<{ conf?: string; date?: unknown }>({});
   const [unpause, setUnpause] = useState(true);
-
-  const { control, handleSubmit, reset, watch } = useForm<BackfillPostBody>({
+  const initialParamsDict = useDagParams(dag.dag_id, true);
+  const { control, handleSubmit, reset, watch } = useForm<BackfillFormProps>({
     defaultValues: {
+      conf: "",
path: airflow-core/src/airflow/ui/src/components/DagActions/RunBackfillForm.tsx
user: login: bbovenzi
body: We need to fetch the conf from the param store in order to keep the Run Parameters form in sync with the json editor.

`const { conf } = useParamStore();`

and

```
  // Automatically reset form when conf is fetched
  useEffect(() => {
    if (conf) {
      reset({ conf });
    }
  }, [conf, reset]);
  ```


```suggestion
      conf,
```
created_at: 2025-04-30T14:35:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -39,14 +42,16 @@ type RunBackfillFormProps = {
 };
 const today = new Date().toISOString().slice(0, 16);
 
+type BackfillFormProps = DagRunTriggerParams & Omit<BackfillPostBody, "dag_run_conf">;
+
 const RunBackfillForm = ({ dag, onClose }: RunBackfillFormProps) => {
   const [errors, setErrors] = useState<{ conf?: string; date?: unknown }>({});
   const [unpause, setUnpause] = useState(true);
-
-  const { control, handleSubmit, reset, watch } = useForm<BackfillPostBody>({
+  const initialParamsDict = useDagParams(dag.dag_id, true);
+  const { control, handleSubmit, reset, watch } = useForm<BackfillFormProps>({
     defaultValues: {
+      conf: "",
path: airflow-core/src/airflow/ui/src/components/DagActions/RunBackfillForm.tsx
user: login: guan404ming
body: Updated.
Thanks for pointing out, I forget to add that.
created_at: 2025-04-30T15:43:45Z
author_association: MEMBER
diff_hunk: @@ -39,14 +42,16 @@ type RunBackfillFormProps = {
 };
 const today = new Date().toISOString().slice(0, 16);
 
+type BackfillFormProps = DagRunTriggerParams & Omit<BackfillPostBody, "dag_run_conf">;
+
 const RunBackfillForm = ({ dag, onClose }: RunBackfillFormProps) => {
   const [errors, setErrors] = useState<{ conf?: string; date?: unknown }>({});
   const [unpause, setUnpause] = useState(true);
-
-  const { control, handleSubmit, reset, watch } = useForm<BackfillPostBody>({
+  const initialParamsDict = useDagParams(dag.dag_id, true);
+  const { control, handleSubmit, reset, watch } = useForm<BackfillFormProps>({
     defaultValues: {
+      conf: "",
path: airflow-core/src/airflow/ui/src/components/DagActions/RunBackfillForm.tsx
user: login: guan404ming
body: btw I have a small question is that should I resolve the conversation after making the changes, or should I wait for the reviewer to resolve it? Not sure about the convention and I think both have its pros and cons, thus to ask the question. 

Thanks in advance for your help!
created_at: 2025-04-30T15:52:06Z
author_association: MEMBER
diff_hunk: @@ -39,14 +42,16 @@ type RunBackfillFormProps = {
 };
 const today = new Date().toISOString().slice(0, 16);
 
+type BackfillFormProps = DagRunTriggerParams & Omit<BackfillPostBody, "dag_run_conf">;
+
 const RunBackfillForm = ({ dag, onClose }: RunBackfillFormProps) => {
   const [errors, setErrors] = useState<{ conf?: string; date?: unknown }>({});
   const [unpause, setUnpause] = useState(true);
-
-  const { control, handleSubmit, reset, watch } = useForm<BackfillPostBody>({
+  const initialParamsDict = useDagParams(dag.dag_id, true);
+  const { control, handleSubmit, reset, watch } = useForm<BackfillFormProps>({
     defaultValues: {
+      conf: "",
path: airflow-core/src/airflow/ui/src/components/DagActions/RunBackfillForm.tsx
user: login: bbovenzi
body: If you implemented what the reviewer suggested. I think resolving it yourself is ok.
created_at: 2025-04-30T17:48:48Z
author_association: CONTRIBUTOR

PR comments:
user: login: bbovenzi
created_at: 2025-04-25T12:40:53Z
body: Good first step! But actually, we need the whole `FlexibleForm` to be available. The json input text is just the fallback if the user doesn't have params specified.

I think we really just need to pull all the conf/params form out of TriggerDAGForm into a shared component to add to the Trigger/Backfill forms.

 My apologies for failing to specify that
author_association: CONTRIBUTOR
user: login: guan404ming
created_at: 2025-04-25T16:30:33Z
body: No worries! I understand and I can try to make pull that form as a shared components
Thanks for clarifying~
author_association: MEMBER
user: login: jscheffl
created_at: 2025-04-25T19:08:08Z
body: I am not a big user of backfills and therefore might not have an insight... but:
Backfill is to trigger runs that had not been executed in the past. They logically should be "similar" to cases when a scheduler would have executed them.
If DAGs are scheduled, then all defined parameters are made with their default values - scheduled jobs do not have a trigger form.

Is it a "we can and therefore we make it" or is there a real functional need to be able to change params when making a backfill? Because it might be different to scheduled runs.
author_association: CONTRIBUTOR
user: login: jscheffl
created_at: 2025-04-25T19:10:01Z
body: Ah, okay looking at https://airflow.apache.org/docs/apache-airflow/2.10.5/cli-and-env-variables-ref.html#backfill ... also previously a conf could be defined to be passed.
author_association: CONTRIBUTOR
user: login: guan404ming
created_at: 2025-04-26T05:32:22Z
body: @bbovenzi I've updated the PR, but I'm not sure that the way how I pass the param is right or not, please let me know if this need any change. Thanks!
author_association: MEMBER
user: login: guan404ming
created_at: 2025-04-28T17:14:48Z
body: Shall we need to hide the field notes, dag run id and logical date in BackFillForm?
author_association: MEMBER
user: login: guan404ming
created_at: 2025-04-28T17:30:02Z
body: Oh, I got it. 
You mean only pull out the conf/params form which is not including the notes, dag run id and  logical date since they're specifically belongs to dag run. I misunderstand what you mean before, I would make a change tomorrow. If my understanding still have some error, please let me know. Thanks a lots~
author_association: MEMBER
user: login: bbovenzi
created_at: 2025-04-28T18:12:05Z
body: > Oh, I got it. You mean only pull out the conf/params form which is not including the notes, dag run id and logical date since they're specifically belongs to dag run. I misunderstand what you mean before, I would make a change tomorrow. If my understanding still have some error, please let me know. Thanks a lots~

Exactly. Only the conf can be shared across each backfill run. Thanks for your patience on the back and forth here.
author_association: CONTRIBUTOR
user: login: guan404ming
created_at: 2025-04-28T18:23:33Z
body: No worries, let me try fix it~
BTW I think the doc is currently lack of details of what we allow user to pass in Backfill `conf`, maybe we could try make it more complete.
author_association: MEMBER
user: login: guan404ming
created_at: 2025-04-30T14:23:14Z
body: @bbovenzi I've fixed this one, please take another look. Thanks!
author_association: MEMBER