diff --git a/packages/module/patternfly-docs/content/extensions/component-groups/examples/ShortcutGrid/ShortcutExample.tsx b/packages/module/patternfly-docs/content/extensions/component-groups/examples/ShortcutGrid/ShortcutExample.tsx
new file mode 100644
index 00000000..5715bf23
--- /dev/null
+++ b/packages/module/patternfly-docs/content/extensions/component-groups/examples/ShortcutGrid/ShortcutExample.tsx
@@ -0,0 +1,4 @@
+import React from 'react';
+import Shortcut from '@patternfly/react-component-groups/dist/dynamic/Shortcut';
+
+export const BasicExample: React.FunctionComponent = () => ;
diff --git a/packages/module/patternfly-docs/content/extensions/component-groups/examples/ShortcutGrid/ShortcutGrid.md b/packages/module/patternfly-docs/content/extensions/component-groups/examples/ShortcutGrid/ShortcutGrid.md
new file mode 100644
index 00000000..ffcb7b65
--- /dev/null
+++ b/packages/module/patternfly-docs/content/extensions/component-groups/examples/ShortcutGrid/ShortcutGrid.md
@@ -0,0 +1,47 @@
+---
+# Sidenav top-level section
+# should be the same for all markdown files
+section: extensions
+subsection: Component groups
+# Sidenav secondary level section
+# should be the same for all markdown files
+id: Shortcut grid
+# Tab (react | react-demos | html | html-demos | design-guidelines | accessibility)
+source: react
+# If you use typescript, the name of the interface to display props for
+# These are found through the sourceProps function provided in patternfly-docs.source.js
+propComponents: [
+ 'ShortcutGrid',
+ 'Shortcut'
+]
+sourceLink: https://github.com/patternfly/react-component-groups/blob/main/packages/module/patternfly-docs/content/extensions/component-groups/examples/ShortcutGrid/ShortcutGrid.md
+---
+
+import ShortcutGrid from '@patternfly/react-component-groups/dist/dynamic/ShortcutGrid';
+import Shortcut from '@patternfly/react-component-groups/dist/dynamic/Shortcut';
+
+A **shortcut grid** component displays keyboard shortcuts with their description in a grid.
+
+## Examples
+
+### Basic shortcut grid
+
+A basic shortcut grid can be used to display shortcuts available to the user together with their description.
+
+You can customize displayed shortcuts using `shortcuts` props. For mouse actions with given shortcuts, there are separate props to be enabled. You can customize showing symbols for control keys using `showSymbols`. The component also accepts all properties of the [grid layout](/layouts/grid).
+
+```js file="./ShortcutGridExample.tsx"
+
+```
+
+### Single shortcut
+
+Shortcut component can be also used outside of the grid.
+
+Appearance of the component can be customized using the `className` property.
+
+```js file="./ShortcutExample.tsx"
+
+```
+
+
diff --git a/packages/module/patternfly-docs/content/extensions/component-groups/examples/ShortcutGrid/ShortcutGridExample.tsx b/packages/module/patternfly-docs/content/extensions/component-groups/examples/ShortcutGrid/ShortcutGridExample.tsx
new file mode 100644
index 00000000..b3a7e591
--- /dev/null
+++ b/packages/module/patternfly-docs/content/extensions/component-groups/examples/ShortcutGrid/ShortcutGridExample.tsx
@@ -0,0 +1,12 @@
+import React from 'react';
+import ShortcutGrid from '@patternfly/react-component-groups/dist/dynamic/ShortcutGrid';
+
+export const BasicExample: React.FunctionComponent = () => (
+
+);
diff --git a/packages/module/src/Shortcut/Shortcut.test.tsx b/packages/module/src/Shortcut/Shortcut.test.tsx
new file mode 100644
index 00000000..0b46be5b
--- /dev/null
+++ b/packages/module/src/Shortcut/Shortcut.test.tsx
@@ -0,0 +1,9 @@
+import React from 'react';
+import { render } from '@testing-library/react';
+import Shortcut from './Shortcut';
+
+describe('Shortcut component', () => {
+ it('should render correctly', () => {
+ expect(render()).toMatchSnapshot();
+ });
+});
diff --git a/packages/module/src/Shortcut/Shortcut.tsx b/packages/module/src/Shortcut/Shortcut.tsx
new file mode 100644
index 00000000..b5fa9bfd
--- /dev/null
+++ b/packages/module/src/Shortcut/Shortcut.tsx
@@ -0,0 +1,105 @@
+import * as React from 'react';
+import { MouseIcon } from '@patternfly/react-icons';
+import { Chip } from '@patternfly/react-core';
+import { createUseStyles } from 'react-jss';
+import clsx from 'clsx';
+
+export interface ShortcutProps {
+ /** Array of shortcut keys */
+ keys: string[];
+ /** Shortcut description */
+ description?: React.ReactNode;
+ /** Indicates whether symbols should be displayed for certain keys */
+ showSymbols?: boolean;
+ /** Show hover in the shortcut */
+ hover?: boolean;
+ /** Show click in the shortcut */
+ click?: boolean;
+ /** Show right click in the shortcut */
+ rightClick?: boolean;
+ /** Show drag in the shortcut */
+ drag?: boolean;
+ /** Show drag and drop in the shortcut */
+ dragAndDrop?: boolean;
+ /** Shortcut className */
+ className?: string;
+}
+
+const symbols = {
+ 'shift': '⇧',
+ 'opt': '⌥',
+ 'cmd': '⌘',
+ 'enter': '↵',
+ 'ctrl': '^',
+ 'caps lock': '⇪',
+ 'tab': '↹',
+ 'win': '⊞',
+ 'backspace': '⌫'
+}
+
+const useStyles = createUseStyles({
+ shortcut: {
+ marginRight: 'var(--pf-v5-global--spacer--lg)'
+ }
+})
+
+const Shortcut: React.FunctionComponent = ({
+ keys = [],
+ description = null,
+ showSymbols = true,
+ hover,
+ click,
+ drag,
+ rightClick,
+ dragAndDrop,
+ className
+}: ShortcutProps) => {
+ const classes = useStyles();
+ const badges = [
+ ...(hover ? [
+
+ Hover
+
+ ] : []),
+ ...keys.map((key) => {
+ const trimmedKey = key.trim().toLowerCase();
+ return(
+
+ {showSymbols && symbols[trimmedKey] ? `${symbols[trimmedKey]} ` : '' }
+ {key.length === 1 ? key.toUpperCase() : key[0].toUpperCase() + key.slice(1).toLowerCase()}
+
+ )}),
+ ...(click ? [
+
+ Click
+
+ ] : []),
+ ...(rightClick ? [
+
+ Right click
+
+ ] : []),
+ ...(drag ? [
+
+ Drag
+
+ ] : []),
+ ...(dragAndDrop ? [
+
+ Drag + Drop
+
+ ] : [])
+ ]
+
+ return (
+ <>
+
+ {badges.length > 0 && badges.reduce((prev, curr) => (
+ <>{[ prev, ' + ', curr ]}>
+ ))}
+
+ {description}
+ >
+ );}
+
+export default Shortcut;
\ No newline at end of file
diff --git a/packages/module/src/Shortcut/__snapshots__/Shortcut.test.tsx.snap b/packages/module/src/Shortcut/__snapshots__/Shortcut.test.tsx.snap
new file mode 100644
index 00000000..2e683f6e
--- /dev/null
+++ b/packages/module/src/Shortcut/__snapshots__/Shortcut.test.tsx.snap
@@ -0,0 +1,216 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Shortcut component should render correctly 1`] = `
+{
+ "asFragment": [Function],
+ "baseElement":
+
+
+
+
+
+ ⌘
+ Cmd
+
+
+
+ +
+
+
+
+ ⇧
+ Shift
+
+
+
+ +
+
+
+ Shortcut description
+
+ ,
+ "container":
+
+
+
+
+ ⌘
+ Cmd
+
+
+
+ +
+
+
+
+ ⇧
+ Shift
+
+
+
+ +
+
+
+ Shortcut description
+
,
+ "debug": [Function],
+ "findAllByAltText": [Function],
+ "findAllByDisplayValue": [Function],
+ "findAllByLabelText": [Function],
+ "findAllByPlaceholderText": [Function],
+ "findAllByRole": [Function],
+ "findAllByTestId": [Function],
+ "findAllByText": [Function],
+ "findAllByTitle": [Function],
+ "findByAltText": [Function],
+ "findByDisplayValue": [Function],
+ "findByLabelText": [Function],
+ "findByPlaceholderText": [Function],
+ "findByRole": [Function],
+ "findByTestId": [Function],
+ "findByText": [Function],
+ "findByTitle": [Function],
+ "getAllByAltText": [Function],
+ "getAllByDisplayValue": [Function],
+ "getAllByLabelText": [Function],
+ "getAllByPlaceholderText": [Function],
+ "getAllByRole": [Function],
+ "getAllByTestId": [Function],
+ "getAllByText": [Function],
+ "getAllByTitle": [Function],
+ "getByAltText": [Function],
+ "getByDisplayValue": [Function],
+ "getByLabelText": [Function],
+ "getByPlaceholderText": [Function],
+ "getByRole": [Function],
+ "getByTestId": [Function],
+ "getByText": [Function],
+ "getByTitle": [Function],
+ "queryAllByAltText": [Function],
+ "queryAllByDisplayValue": [Function],
+ "queryAllByLabelText": [Function],
+ "queryAllByPlaceholderText": [Function],
+ "queryAllByRole": [Function],
+ "queryAllByTestId": [Function],
+ "queryAllByText": [Function],
+ "queryAllByTitle": [Function],
+ "queryByAltText": [Function],
+ "queryByDisplayValue": [Function],
+ "queryByLabelText": [Function],
+ "queryByPlaceholderText": [Function],
+ "queryByRole": [Function],
+ "queryByTestId": [Function],
+ "queryByText": [Function],
+ "queryByTitle": [Function],
+ "rerender": [Function],
+ "unmount": [Function],
+}
+`;
diff --git a/packages/module/src/Shortcut/index.ts b/packages/module/src/Shortcut/index.ts
new file mode 100644
index 00000000..6fff158c
--- /dev/null
+++ b/packages/module/src/Shortcut/index.ts
@@ -0,0 +1,2 @@
+export { default } from './Shortcut';
+export * from './Shortcut';
diff --git a/packages/module/src/ShortcutGrid/ShortcutGrid.test.tsx b/packages/module/src/ShortcutGrid/ShortcutGrid.test.tsx
new file mode 100644
index 00000000..6146aaa7
--- /dev/null
+++ b/packages/module/src/ShortcutGrid/ShortcutGrid.test.tsx
@@ -0,0 +1,17 @@
+import React from 'react';
+import { render } from '@testing-library/react';
+import ShortcutGrid from './ShortcutGrid';
+
+describe('ShortcutGrid component', () => {
+ it('should render correctly', () => {
+ expect(render(
+
+ )).toMatchSnapshot();
+ });
+});
diff --git a/packages/module/src/ShortcutGrid/ShortcutGrid.tsx b/packages/module/src/ShortcutGrid/ShortcutGrid.tsx
new file mode 100644
index 00000000..8be0206d
--- /dev/null
+++ b/packages/module/src/ShortcutGrid/ShortcutGrid.tsx
@@ -0,0 +1,37 @@
+import * as React from 'react';
+import { createUseStyles } from 'react-jss';
+import Shortcut, { ShortcutProps } from '../Shortcut/Shortcut';
+import { Grid, GridItem, GridItemProps, GridProps } from '@patternfly/react-core';
+
+export interface ShortcutGridProps extends GridProps {
+ /** Array of shortcuts to be displayed in the grid */
+ shortcuts: ShortcutProps[];
+ /** Shortcut GridItem props */
+ gridItemProps?: GridItemProps
+}
+
+const useStyles = createUseStyles({
+ shortcutGridItem: {
+ textAlign: 'right',
+ marginRight: 'var(--pf-v5-global--spacer--sm)'
+ }
+})
+
+const ShortcutGrid: React.FunctionComponent = ({ shortcuts, gridItemProps, ...rest }: ShortcutGridProps) => {
+ const classes = useStyles();
+ return (
+
+ {shortcuts.map((shortcut, index) => {
+ const { description, ...props } = shortcut;
+ return(
+
+
+
+
+ {description}
+
+ )})}
+ )
+}
+
+export default ShortcutGrid;
\ No newline at end of file
diff --git a/packages/module/src/ShortcutGrid/__snapshots__/ShortcutGrid.test.tsx.snap b/packages/module/src/ShortcutGrid/__snapshots__/ShortcutGrid.test.tsx.snap
new file mode 100644
index 00000000..886a1c1e
--- /dev/null
+++ b/packages/module/src/ShortcutGrid/__snapshots__/ShortcutGrid.test.tsx.snap
@@ -0,0 +1,444 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ShortcutGrid component should render correctly 1`] = `
+{
+ "asFragment": [Function],
+ "baseElement":
+
+
+
+
+
+
+
+ ⌘
+ Cmd
+
+
+
+ +
+
+
+
+ ⇧
+ Shift
+
+
+
+ +
+
+
+
+ T
+
+
+
+
+
+
+ Open in a new tab
+
+
+
+
+
+
+ ⌥
+ Opt
+
+
+
+ +
+
+
+
+ N
+
+
+
+
+
+
+ Open new page
+
+
+
+
+
+
+ ^
+ Ctrl
+
+
+
+ +
+
+
+
+
+ Move object
+
+
+
+ ,
+ "container":
+
+
+
+
+
+
+ ⌘
+ Cmd
+
+
+
+ +
+
+
+
+ ⇧
+ Shift
+
+
+
+ +
+
+
+
+ T
+
+
+
+
+
+
+ Open in a new tab
+
+
+
+
+
+
+ ⌥
+ Opt
+
+
+
+ +
+
+
+
+ N
+
+
+
+
+
+
+ Open new page
+
+
+
+
+
+
+ ^
+ Ctrl
+
+
+
+ +
+
+
+
+
+ Move object
+
+
+
,
+ "debug": [Function],
+ "findAllByAltText": [Function],
+ "findAllByDisplayValue": [Function],
+ "findAllByLabelText": [Function],
+ "findAllByPlaceholderText": [Function],
+ "findAllByRole": [Function],
+ "findAllByTestId": [Function],
+ "findAllByText": [Function],
+ "findAllByTitle": [Function],
+ "findByAltText": [Function],
+ "findByDisplayValue": [Function],
+ "findByLabelText": [Function],
+ "findByPlaceholderText": [Function],
+ "findByRole": [Function],
+ "findByTestId": [Function],
+ "findByText": [Function],
+ "findByTitle": [Function],
+ "getAllByAltText": [Function],
+ "getAllByDisplayValue": [Function],
+ "getAllByLabelText": [Function],
+ "getAllByPlaceholderText": [Function],
+ "getAllByRole": [Function],
+ "getAllByTestId": [Function],
+ "getAllByText": [Function],
+ "getAllByTitle": [Function],
+ "getByAltText": [Function],
+ "getByDisplayValue": [Function],
+ "getByLabelText": [Function],
+ "getByPlaceholderText": [Function],
+ "getByRole": [Function],
+ "getByTestId": [Function],
+ "getByText": [Function],
+ "getByTitle": [Function],
+ "queryAllByAltText": [Function],
+ "queryAllByDisplayValue": [Function],
+ "queryAllByLabelText": [Function],
+ "queryAllByPlaceholderText": [Function],
+ "queryAllByRole": [Function],
+ "queryAllByTestId": [Function],
+ "queryAllByText": [Function],
+ "queryAllByTitle": [Function],
+ "queryByAltText": [Function],
+ "queryByDisplayValue": [Function],
+ "queryByLabelText": [Function],
+ "queryByPlaceholderText": [Function],
+ "queryByRole": [Function],
+ "queryByTestId": [Function],
+ "queryByText": [Function],
+ "queryByTitle": [Function],
+ "rerender": [Function],
+ "unmount": [Function],
+}
+`;
diff --git a/packages/module/src/ShortcutGrid/index.ts b/packages/module/src/ShortcutGrid/index.ts
new file mode 100644
index 00000000..4ab64b8b
--- /dev/null
+++ b/packages/module/src/ShortcutGrid/index.ts
@@ -0,0 +1,2 @@
+export { default } from './ShortcutGrid';
+export * from './ShortcutGrid';
diff --git a/packages/module/src/index.ts b/packages/module/src/index.ts
index c208f474..a10cdf31 100644
--- a/packages/module/src/index.ts
+++ b/packages/module/src/index.ts
@@ -42,6 +42,12 @@ export * from './NotAuthorized';
export { default as NotFoundIcon } from './NotFoundIcon';
export * from './NotFoundIcon';
+export { default as Shortcut } from './Shortcut';
+export * from './Shortcut';
+
+export { default as ShortcutGrid } from './ShortcutGrid';
+export * from './ShortcutGrid';
+
export { default as SkeletonTable } from './SkeletonTable';
export * from './SkeletonTable';