diff --git a/packages/grafana-ui/src/components/Forms/FieldArray.mdx b/packages/grafana-ui/src/components/Forms/FieldArray.mdx
new file mode 100644
index 00000000000..465b68acc4f
--- /dev/null
+++ b/packages/grafana-ui/src/components/Forms/FieldArray.mdx
@@ -0,0 +1,40 @@
+import { Meta, Props } from '@storybook/addon-docs/blocks';
+import { FieldArray } from './FieldArray';
+
+
+
+# FieldArray
+
+`FieldArray` provides a way to render a list of dynamic inputs. It exposes the functionality of `useFieldArray` in [react-hook-form](https://react-hook-form.com/advanced-usage/#FieldArrays). `FieldArray` must be wrapped at some level by a `
;
+```
+
+### FieldArray API
+
+The `FieldArray` component exposes its API via render prop. Properties exposed are: `fields`, `append`, `prepend`, `remove`, `swap`, `move`, `insert`
+
+### Props
+
+
diff --git a/packages/grafana-ui/src/components/Forms/FieldArray.story.tsx b/packages/grafana-ui/src/components/Forms/FieldArray.story.tsx
new file mode 100644
index 00000000000..fd4bccd6820
--- /dev/null
+++ b/packages/grafana-ui/src/components/Forms/FieldArray.story.tsx
@@ -0,0 +1,52 @@
+import React from 'react';
+import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
+import { withStoryContainer } from '../../utils/storybook/withStoryContainer';
+import { Form, Input, Button, HorizontalGroup } from '@grafana/ui';
+import { FieldArray } from './FieldArray';
+import mdx from './FieldArray.mdx';
+
+export default {
+ title: 'Forms/FieldArray',
+ component: FieldArray,
+ decorators: [withStoryContainer, withCenteredStory],
+ parameters: {
+ docs: {
+ page: mdx,
+ },
+ },
+};
+
+export const simple = () => {
+ const defaultValues = {
+ people: [{ firstName: 'Janis', lastName: 'Joplin' }],
+ };
+ return (
+
+ );
+};
diff --git a/packages/grafana-ui/src/components/Forms/FieldArray.tsx b/packages/grafana-ui/src/components/Forms/FieldArray.tsx
new file mode 100644
index 00000000000..d9f07bb5e1c
--- /dev/null
+++ b/packages/grafana-ui/src/components/Forms/FieldArray.tsx
@@ -0,0 +1,15 @@
+import { FC } from 'react';
+import { useFieldArray, UseFieldArrayProps } from 'react-hook-form';
+import { FieldArrayApi } from '../../types';
+
+export interface FieldArrayProps extends UseFieldArrayProps {
+ children: (api: FieldArrayApi) => JSX.Element;
+}
+
+export const FieldArray: FC = ({ name, control, children }) => {
+ const { fields, append, prepend, remove, swap, move, insert } = useFieldArray({
+ control,
+ name,
+ });
+ return children({ fields, append, prepend, remove, swap, move, insert });
+};
diff --git a/packages/grafana-ui/src/types/forms.ts b/packages/grafana-ui/src/types/forms.ts
index 9442095440f..8088399e2f5 100644
--- a/packages/grafana-ui/src/types/forms.ts
+++ b/packages/grafana-ui/src/types/forms.ts
@@ -1,7 +1,19 @@
-import { FormContextValues } from 'react-hook-form';
+import { FormContextValues, FieldValues, ArrayField } from 'react-hook-form';
export { OnSubmit as FormsOnSubmit, FieldErrors as FormFieldErrors } from 'react-hook-form';
export type FormAPI = Pick<
FormContextValues,
'register' | 'errors' | 'control' | 'formState' | 'getValues' | 'watch'
>;
+
+type FieldArrayValue = Partial | Array>;
+
+export interface FieldArrayApi {
+ fields: Array>>;
+ append: (value: FieldArrayValue) => void;
+ prepend: (value: FieldArrayValue) => void;
+ remove: (index?: number | number[]) => void;
+ swap: (indexA: number, indexB: number) => void;
+ move: (from: number, to: number) => void;
+ insert: (index: number, value: FieldArrayValue) => void;
+}