diff --git a/src/authz-module/components/AuthZLayout.tsx b/src/authz-module/components/AuthZLayout.tsx
new file mode 100644
index 0000000..9845b90
--- /dev/null
+++ b/src/authz-module/components/AuthZLayout.tsx
@@ -0,0 +1,27 @@
+import { ReactNode } from 'react';
+import { StudioHeader } from '@edx/frontend-component-header';
+import AuthZTitle, { AuthZTitleProps } from './AuthZTitle';
+
+interface AuthZLayoutProps extends AuthZTitleProps {
+ children: ReactNode;
+ context: {
+ id: string;
+ org: string;
+ title: string;
+ }
+}
+
+const AuthZLayout = ({ children, context, ...props }: AuthZLayoutProps) => (
+ <>
+
+
+ {children}
+ >
+
+);
+
+export default AuthZLayout;
diff --git a/src/authz-module/components/AuthZTitle.test.tsx b/src/authz-module/components/AuthZTitle.test.tsx
new file mode 100644
index 0000000..460cf88
--- /dev/null
+++ b/src/authz-module/components/AuthZTitle.test.tsx
@@ -0,0 +1,62 @@
+import React from 'react';
+import { render, screen, fireEvent } from '@testing-library/react';
+import AuthZTitle, { AuthZTitleProps } from './AuthZTitle';
+
+describe('AuthZTitle', () => {
+ const defaultProps: AuthZTitleProps = {
+ activeLabel: 'Current Page',
+ pageTitle: 'Page Title',
+ pageSubtitle: 'Page Subtitle',
+ };
+
+ it('renders without optional fields', () => {
+ render();
+ expect(screen.getByText(defaultProps.activeLabel)).toBeInTheDocument();
+ expect(screen.getByText(defaultProps.pageTitle)).toBeInTheDocument();
+ expect(screen.getByText(defaultProps.pageSubtitle as string)).toBeInTheDocument();
+ });
+
+ it('renders breadcrumb with links and active label', () => {
+ const navLinks = [
+ { label: 'Root', to: '/' },
+ { label: 'Section', to: '/section' },
+ ];
+
+ render();
+
+ navLinks.forEach(({ label }) => {
+ expect(screen.getByText(label)).toBeInTheDocument();
+ });
+
+ expect(screen.getByText(defaultProps.activeLabel)).toBeInTheDocument();
+ });
+
+ it('renders page title', () => {
+ render();
+ expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent(defaultProps.pageTitle);
+ });
+
+ it('renders page subtitle as ReactNode', () => {
+ const subtitleNode =
Custom Subtitle
;
+ render();
+ expect(screen.getByTestId('custom-subtitle')).toBeInTheDocument();
+ });
+
+ it('renders action buttons and triggers onClick', () => {
+ const onClick1 = jest.fn();
+ const onClick2 = jest.fn();
+ const actions = [
+ { label: 'Save', onClick: onClick1 },
+ { label: 'Cancel', onClick: onClick2 },
+ ];
+
+ render();
+
+ actions.forEach(({ label, onClick }) => {
+ const button = screen.getByRole('button', { name: label });
+ expect(button).toBeInTheDocument();
+ fireEvent.click(button);
+ expect(onClick).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/src/authz-module/components/AuthZTitle.tsx b/src/authz-module/components/AuthZTitle.tsx
new file mode 100644
index 0000000..6224ea9
--- /dev/null
+++ b/src/authz-module/components/AuthZTitle.tsx
@@ -0,0 +1,50 @@
+import { ReactNode } from 'react';
+import {
+ Breadcrumb, Col, Container, Row, Button, Badge,
+} from '@openedx/paragon';
+
+interface BreadcrumbLink {
+ label: string;
+ to?: string;
+}
+
+interface Action {
+ label: string;
+ onClick: () => void;
+}
+
+export interface AuthZTitleProps {
+ activeLabel: string;
+ pageTitle: string;
+ pageSubtitle: string | ReactNode;
+ navLinks?: BreadcrumbLink[];
+ actions?: Action[];
+}
+
+const AuthZTitle = ({
+ activeLabel, navLinks = [], pageTitle, pageSubtitle, actions = [],
+}: AuthZTitleProps) => (
+
+
+
+
+ {pageTitle}
+ {typeof pageSubtitle === 'string'
+ ? {pageSubtitle}
+ : pageSubtitle}
+
+
+
+ {
+ actions.map(({ label, onClick }) => )
+ }
+
+
+
+
+);
+
+export default AuthZTitle;
diff --git a/src/authz-module/index.scss b/src/authz-module/index.scss
new file mode 100644
index 0000000..4aada25
--- /dev/null
+++ b/src/authz-module/index.scss
@@ -0,0 +1,6 @@
+.authz-libraries {
+ .pgn__breadcrumb li:first-child a {
+ color: var(--pgn-color-breadcrumb-active);
+ text-decoration: none;
+ }
+}