diff --git a/packages/react/lib/RadioField/index.js b/packages/react/lib/RadioField/index.js
new file mode 100644
index 000000000..ebfcd3b82
--- /dev/null
+++ b/packages/react/lib/RadioField/index.js
@@ -0,0 +1,110 @@
+import { forwardRef, useReducer, useRef } from 'react';
+import PropTypes from 'prop-types';
+import { classNames, mockState } from '@junipero/core';
+
+const RadioField = forwardRef(({
+ className,
+ disabled = false,
+ id,
+ name,
+ options = [],
+ value,
+ parseValue = val => val.value ?? val,
+ parseTitle = val => val?.title ?? val?.toString?.(),
+ parseDescription = val => val?.description || '',
+ onChange = () => {},
+ ...rest
+}, ref) => {
+ const inputRefs = useRef([]);
+ const innerRefs = useRef([]);
+ const wrapperRef = useRef();
+ const [state, dispatch] = useReducer(mockState, {
+ focused: null,
+ dirty: false,
+ value,
+ valid: false,
+ });
+
+ const isChecked = option =>
+ state.value === option || state.value === parseValue(option);
+
+ const onChange_ = option => {
+ dispatch({ value: option, valid: true, dirty: true });
+ onChange({ value: parseValue(option), valid: true });
+ };
+
+ const onKeyDown = (option, e) => {
+ if (
+ state.value !== option &&
+ state.value !== parseValue(option) &&
+ (e.key === 'Enter' || e.key === ' ')
+ ) {
+ onChange_(option);
+ }
+
+ return true;
+ };
+
+ return (
+
+ { options.map((option, index) => (
+
+ ))}
+
+ );
+});
+
+RadioField.propTypes = {
+ disabled: PropTypes.bool,
+ className: PropTypes.string,
+ onChange: PropTypes.func,
+ options: PropTypes.array,
+ parseValue: PropTypes.func,
+ parseDescription: PropTypes.func,
+ parseTitle: PropTypes.func,
+ value: PropTypes.any,
+ id: PropTypes.string,
+ name: PropTypes.string,
+};
+
+export default RadioField;
diff --git a/packages/react/lib/RadioField/index.stories.js b/packages/react/lib/RadioField/index.stories.js
new file mode 100644
index 000000000..8a10792cc
--- /dev/null
+++ b/packages/react/lib/RadioField/index.stories.js
@@ -0,0 +1,25 @@
+import { action } from '@storybook/addon-actions';
+
+import RadioField from '.';
+
+export default { title: 'react/RadioField' };
+const basicOptions = [
+ { title: 'Apple', value: 'Apple' },
+ { title: 'Pear', value: 'Pear' },
+ { title: 'Orange', value: 'Orange' },
+];
+
+const basicOptionsOneDisabled = [
+ { title: 'Apple', value: 'Apple' },
+ { title: 'Pear', value: 'Pear' },
+ { title: 'Orange', value: 'Orange', disabled: true },
+];
+
+const withDescriptions = [
+ { title: 'Apple', value: 'Apple', description: 'This is a description' },
+ { title: 'Pear', value: 'Pear', description: 'This is a description' },
+ { title: 'Orange', value: 'Orange', description: 'This is a description' },
+];
+export const basic = () => (
+
+);
diff --git a/packages/react/lib/RadioField/index.test.js b/packages/react/lib/RadioField/index.test.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/theme/lib/RadioField.sass b/packages/theme/lib/RadioField.sass
new file mode 100644
index 000000000..67d64faa0
--- /dev/null
+++ b/packages/theme/lib/RadioField.sass
@@ -0,0 +1 @@
+@use "colors" as colors
\ No newline at end of file