diff --git a/components/Dialogs/Confirm/HorizontalButtons.tsx b/components/Dialogs/Confirm/HorizontalButtons.tsx
new file mode 100644
index 00000000..aa4765fd
--- /dev/null
+++ b/components/Dialogs/Confirm/HorizontalButtons.tsx
@@ -0,0 +1,77 @@
+import React from 'react';
+
+import { Colors, Text, TouchableOpacity, View } from 'react-native-ui-lib';
+
+import { useUpdate } from './context';
+
+export interface ConfirmDialogHorizontalButtonsProps {
+ /**
+ * The function to call when the confirm button is pressed
+ */
+ onConfirm: () => void;
+
+ /**
+ * The text to display on the confirm button
+ * @default 'Yes'
+ */
+ confirmText?: string;
+
+ /**
+ * The text to display on the cancel button
+ * @default 'No'
+ */
+ cancelText?: string;
+
+ /**
+ * The color of the confirm button
+ * @default '$textSuccess'
+ */
+ confirmColor?: keyof typeof Colors;
+
+ /**
+ * The color of the cancel button
+ * @default '$textDefault'
+ */
+ cancelColor?: keyof typeof Colors;
+}
+
+export const HorizontalButtons = ({
+ onConfirm,
+ cancelText = 'No',
+ confirmText = 'Yes',
+ cancelColor = '$textDanger',
+ confirmColor = '$textSuccess',
+}: ConfirmDialogHorizontalButtonsProps) => {
+ const update = useUpdate();
+
+ const handleClose = () => update({ visible: false });
+
+ const handleConfirm = () => {
+ handleClose();
+ onConfirm();
+ };
+
+ return (
+
+
+
+ {cancelText}
+
+
+
+
+ {confirmText}
+
+
+
+ );
+};
+HorizontalButtons.displayName = 'ConfirmDialog.HorizontalButtons';
diff --git a/components/Dialogs/Confirm/Root.tsx b/components/Dialogs/Confirm/Root.tsx
new file mode 100644
index 00000000..c509daad
--- /dev/null
+++ b/components/Dialogs/Confirm/Root.tsx
@@ -0,0 +1,72 @@
+import React from 'react';
+
+import { Card, Dialog } from 'react-native-ui-lib';
+
+import { useContext, useUpdate } from './context';
+
+export interface ConfirmDialogProps {
+ /**
+ * The description text if title is not enough
+ */
+ children?: React.ReactNode;
+}
+
+export type ConfirmDialogRef = {
+ /**
+ * Open the dialog
+ */
+ open: () => void;
+};
+
+/**
+ * A dialog that asks the user to confirm an action
+ *
+ * @example
+ * ```tsx
+ * const ref = React.useRef(null);
+ *
+ * const handlePress = () => {
+ * ref.current?.open();
+ * };
+ *
+ * const handleDialogConfirm = () => {
+ * ref.current?.close();
+ * // do something
+ * };
+ *
+ * return (
+ * <>
+ *
+ * Save Session
+ * Are you sure you want to save this session?
+ *
+ *
+ *
+ * >
+ * );
+ * ```
+ */
+export const Root = React.forwardRef(
+ ({ children }, ref) => {
+ const { visible } = useContext();
+ const update = useUpdate();
+
+ const handleOpen = () => update({ visible: true });
+ const handleClose = () => update({ visible: false });
+
+ React.useImperativeHandle(ref, () => ({
+ open: handleOpen,
+ }));
+
+ return (
+
+ );
+ }
+);
+Root.displayName = 'ConfirmDialog.Root';
diff --git a/components/Dialogs/Confirm/Title.tsx b/components/Dialogs/Confirm/Title.tsx
new file mode 100644
index 00000000..d1e6cc4c
--- /dev/null
+++ b/components/Dialogs/Confirm/Title.tsx
@@ -0,0 +1,9 @@
+import React from 'react';
+
+import { Text as RNText } from 'react-native';
+import { Text, TextProps } from 'react-native-ui-lib';
+
+export const Title = React.forwardRef((props, ref) => (
+
+));
+Title.displayName = 'ConfirmDialog.Title';
diff --git a/components/Dialogs/Confirm/VerticalButtons.tsx b/components/Dialogs/Confirm/VerticalButtons.tsx
new file mode 100644
index 00000000..07a45eef
--- /dev/null
+++ b/components/Dialogs/Confirm/VerticalButtons.tsx
@@ -0,0 +1,78 @@
+import React from 'react';
+
+import { Button, Colors, View } from 'react-native-ui-lib';
+
+import { useUpdate } from './context';
+
+export interface ConfirmDialogVerticalButtonsProps {
+ /**
+ * The function to call when the confirm button is pressed
+ */
+ onConfirm: () => void;
+
+ /**
+ * The text to display on the confirm button
+ * @default 'Yes'
+ */
+ confirmText?: string;
+
+ /**
+ * The text to display on the cancel button
+ * @default 'No'
+ */
+ cancelText?: string;
+
+ /**
+ * The color of the confirm button
+ * @default '$textSuccess'
+ */
+ confirmColor?: keyof typeof Colors;
+
+ /**
+ * The color of the cancel button
+ * @default '$textDefault'
+ */
+ cancelColor?: keyof typeof Colors;
+
+ /**
+ * If true, the buttons will be reversed
+ * @default false
+ */
+ reversed?: boolean;
+}
+
+export const VerticalButtons = ({
+ onConfirm,
+ cancelText = 'No',
+ confirmText = 'Yes',
+ cancelColor = '$textDanger',
+ confirmColor = '$textSuccess',
+ reversed = false,
+}: ConfirmDialogVerticalButtonsProps) => {
+ const update = useUpdate();
+
+ const handleClose = () => update({ visible: false });
+
+ const handleConfirm = () => {
+ handleClose();
+ onConfirm();
+ };
+
+ return (
+
+
+
+
+ );
+};
+VerticalButtons.displayName = 'ConfirmDialog.VerticalButtons';
diff --git a/components/Dialogs/Confirm/context.ts b/components/Dialogs/Confirm/context.ts
new file mode 100644
index 00000000..0886434e
--- /dev/null
+++ b/components/Dialogs/Confirm/context.ts
@@ -0,0 +1,7 @@
+import { contextBuilder } from '@htk/utils/react/contextBuilder';
+
+const [ConfirmDialogProvider, useContext, useDispatch, useUpdate] = contextBuilder({
+ visible: false,
+});
+
+export { ConfirmDialogProvider, useContext, useDispatch, useUpdate };
diff --git a/components/Dialogs/Confirm/index.tsx b/components/Dialogs/Confirm/index.tsx
new file mode 100644
index 00000000..b7498de7
--- /dev/null
+++ b/components/Dialogs/Confirm/index.tsx
@@ -0,0 +1,18 @@
+import React from 'react';
+
+import { ConfirmDialogProvider } from './context';
+import { HorizontalButtons } from './HorizontalButtons';
+import { ConfirmDialogProps, ConfirmDialogRef, Root } from './Root';
+import { Title } from './Title';
+import { VerticalButtons } from './VerticalButtons';
+
+export { ConfirmDialogRef, ConfirmDialogProps };
+
+export const ConfirmDialog = Object.assign(
+ React.forwardRef((props, ref) => (
+
+
+
+ )),
+ { Title, HorizontalButtons, VerticalButtons }
+);
diff --git a/components/Dialogs/index.ts b/components/Dialogs/index.ts
new file mode 100644
index 00000000..d8e1c97b
--- /dev/null
+++ b/components/Dialogs/index.ts
@@ -0,0 +1 @@
+export * from './Confirm';
diff --git a/components/README.md b/components/README.md
new file mode 100644
index 00000000..cd4339f6
--- /dev/null
+++ b/components/README.md
@@ -0,0 +1,118 @@
+# HTK React Native Components
+
+These components are reusable parts for all Expo/React Native projects.
+
+## Required NPM Packages
+- `react-native-ui-lib` - A versatile library that offers a variety of useful
+ components and utilities.
+
+## Rules
+
+The following rules must be adhered to:
+
+### Folder Structure
+Each component or component group should have its own separate folder.
+
+**Reason**:
+A component may be too large to reside in a single file and may need to be
+split into multiple files to enhance performance and readability. The folder
+name should reflect the component name. The folder may contain multiple
+subcomponents that are private to the component itself.
+
+:x: Bad:
+```sh
+./components/Button.tsx
+```
+
+:white_check_mark: Good:
+```sh
+./components/Button/index.tsx
+```
+
+### Filetype
+All files must be in TypeScript to provide error checking and autocompletion.
+The correct extension for TypeScript JSX files is .tsx.
+
+### TypeScript Types
+When using props in a component:
+- Use `interface`
+- The name must be `Props`
+- `export` the interface
+
+**Reason**:
+Interfaces are easier to extend. There might be a need to wrap the component
+for special cases, utilizing the component's props.
+
+:x: Bad
+```tsx
+// Bad because:
+// - It does not use `interface`
+// - The name is too generic
+// - It is not exported
+type Props = {
+ // ...
+}
+
+export function Button(props: Props) {
+ // ...
+}
+```
+
+:white_check_mark: Good:
+```tsx
+export interface ButtonProps {
+ // ...
+}
+
+export function Button(props: Props) {
+ // ...
+}
+```
+
+### Extensive docstrings
+**Reason**:
+To improve documentation and assist the team in easily utilizing the component,
+each prop and the component itself must have docstrings.
+
+:x: Bad
+```tsx
+export interface ButtonProps {
+ label: string;
+ onPress?: () => void;
+}
+
+export function Button(props: Props) {
+ // ...
+}
+```
+
+:white_check_mark: Good:
+```tsx
+export interface ButtonProps {
+ /**
+ * Text to displayed inside the button.
+ */
+ label: string;
+
+ /**
+ * Callback function that is called when the button is pressed.
+ */
+ onPress?: () => void;
+}
+
+/**
+ * Button Component
+ *
+ * @example
+ * ```tsx
+ * const handlePress = () => {
+ * // ...
+ * };
+ * return ;
+ *
+ * ```
+ */
+export function Button(props: Props) {
+ // ...
+}
+```
diff --git a/utils/react/contextBuilder.ts b/utils/react/contextBuilder.ts
new file mode 100644
index 00000000..1507cc0f
--- /dev/null
+++ b/utils/react/contextBuilder.ts
@@ -0,0 +1,65 @@
+import React from 'react';
+
+type ContextAction = { type: 'UPDATE'; payload: Partial } | { type: 'RESET' };
+type IProvider = React.FC<{ value?: Partial; children: React.ReactNode }>;
+type IuseContext = () => T;
+type IuseContextDispatch = () => React.Dispatch>;
+type IuseUpdate = () => (payload: Partial) => void;
+
+export function contextBuilder(
+ defaultValue: T
+): [IProvider, IuseContext, IuseContextDispatch, IuseUpdate] {
+ const Context = React.createContext(defaultValue);
+ const DispatchContext = React.createContext>>(
+ () => {}
+ );
+
+ const reducer: React.Reducer> = (state, action) => {
+ switch (action.type) {
+ case 'UPDATE':
+ return { ...state, ...action.payload };
+ case 'RESET':
+ return defaultValue;
+ }
+ };
+
+ const useContext = () => {
+ const context = React.useContext(Context);
+ if (context === undefined) {
+ throw new Error('useContext must be used within a Provider');
+ }
+ return context;
+ };
+
+ const useDispatch = () => {
+ const dispatch = React.useContext(DispatchContext);
+ if (dispatch === undefined) {
+ throw new Error('useDispatch must be used within a Provider');
+ }
+ return dispatch;
+ };
+
+ const useUpdate = () => {
+ const dispatch = useDispatch();
+
+ return (payload: Partial) => dispatch({ type: 'UPDATE', payload });
+ };
+
+ const Provider = ({
+ value = {},
+ children,
+ }: {
+ value?: Partial;
+ children: React.ReactNode;
+ }) => {
+ const [state, dispatch] = React.useReducer(reducer, defaultValue);
+
+ const context = React.useMemo(() => ({ ...state, ...value }), [state, value]);
+
+ return React.createElement(DispatchContext.Provider, { value: dispatch }, [
+ React.createElement(Context.Provider, { value: context }, children),
+ ]);
+ };
+
+ return [Provider, useContext, useDispatch, useUpdate];
+}