+
+ ),
+});
diff --git a/packages/ui/src/components/Combobox/Combobox.tsx b/packages/ui/src/components/Combobox/Combobox.tsx
new file mode 100644
index 0000000000..b59dffb320
--- /dev/null
+++ b/packages/ui/src/components/Combobox/Combobox.tsx
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2026 The Backstage Authors
+ *
+ * Licensed 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 { forwardRef, useEffect } from 'react';
+import { ComboBox as AriaComboBox } from 'react-aria-components';
+import { useFilter } from 'react-aria';
+import { ComboboxProps } from './types';
+import { useDefinition } from '../../hooks/useDefinition';
+import { ComboboxDefinition } from './definition';
+import { Popover } from '../Popover';
+import { FieldLabel } from '../FieldLabel';
+import { FieldError } from '../FieldError';
+import { ComboboxInput } from './ComboboxInput';
+import { ComboboxListBox } from './ComboboxListBox';
+
+/**
+ * A text input combined with a dropdown list of options. The user can type to filter
+ * suggestions, navigate with the keyboard, and pick a value. With `allowsCustomValue`
+ * the typed text can be committed even if no option matches.
+ *
+ * @public
+ */
+export const Combobox = forwardRef(
+ (props, ref) => {
+ const { contains } = useFilter({ sensitivity: 'base' });
+ const { ownProps, restProps, dataAttributes } = useDefinition(
+ ComboboxDefinition,
+ props,
+ );
+ const {
+ classes,
+ label,
+ description,
+ options,
+ icon,
+ placeholder,
+ isRequired,
+ secondaryLabel,
+ } = ownProps;
+
+ const ariaLabel = restProps['aria-label'];
+ const ariaLabelledBy = restProps['aria-labelledby'];
+
+ useEffect(() => {
+ if (!label && !ariaLabel && !ariaLabelledBy) {
+ console.warn(
+ 'Combobox requires either a visible label, aria-label, or aria-labelledby for accessibility',
+ );
+ }
+ }, [label, ariaLabel, ariaLabelledBy]);
+
+ const secondaryLabelText =
+ secondaryLabel || (isRequired ? 'Required' : null);
+
+ return (
+
+
+
+
+
+
+
+
+ );
+ },
+);
+
+Combobox.displayName = 'Combobox';
diff --git a/packages/ui/src/components/Combobox/ComboboxInput.tsx b/packages/ui/src/components/Combobox/ComboboxInput.tsx
new file mode 100644
index 0000000000..ce7acfff71
--- /dev/null
+++ b/packages/ui/src/components/Combobox/ComboboxInput.tsx
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2026 The Backstage Authors
+ *
+ * Licensed 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 { Button, Group, Input } from 'react-aria-components';
+import { RiArrowDownSLine } from '@remixicon/react';
+import { useDefinition } from '../../hooks/useDefinition';
+import { ComboboxInputDefinition } from './definition';
+import type { ComboboxInputOwnProps } from './types';
+
+export function ComboboxInput(props: ComboboxInputOwnProps) {
+ const { ownProps, dataAttributes } = useDefinition(
+ ComboboxInputDefinition,
+ props,
+ );
+ const { classes, icon, placeholder } = ownProps;
+
+ return (
+
+ {icon ? (
+
+ {icon}
+
+ ) : null}
+
+
+
+ );
+}
diff --git a/packages/ui/src/components/Combobox/ComboboxListBox.tsx b/packages/ui/src/components/Combobox/ComboboxListBox.tsx
new file mode 100644
index 0000000000..ae65bd8da3
--- /dev/null
+++ b/packages/ui/src/components/Combobox/ComboboxListBox.tsx
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2026 The Backstage Authors
+ *
+ * Licensed 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 {
+ ListBox,
+ ListBoxItem,
+ ListBoxSection,
+ Header,
+ Text,
+} from 'react-aria-components';
+import { RiCheckLine } from '@remixicon/react';
+import { useDefinition } from '../../hooks/useDefinition';
+import {
+ ComboboxListBoxDefinition,
+ ComboboxListBoxItemDefinition,
+ ComboboxSectionDefinition,
+} from './definition';
+import type { Option, OptionSection, ComboboxListBoxOwnProps } from './types';
+
+const NoResults = () => {
+ const { ownProps } = useDefinition(ComboboxListBoxDefinition, {});
+ const { classes } = ownProps;
+
+ return