diff --git a/packages/html/html/src/components/media-duration-display.ts b/packages/html/html/src/components/media-duration-display.ts
new file mode 100644
index 00000000..5fa05da4
--- /dev/null
+++ b/packages/html/html/src/components/media-duration-display.ts
@@ -0,0 +1,124 @@
+import {
+ toConnectedHTMLComponent,
+ StateHook,
+ PropsHook,
+} from '../utils/component-factory';
+import { durationDisplayStateDefinition } from '@vjs-10/media-store';
+import { namedNodeMapToObject } from '../utils/element-utils.js';
+
+export function getTemplateHTML(
+ this: typeof DurationDisplayBase,
+ _attrs: Record,
+ _props: Record = {},
+) {
+ return /* html */ `
+
+ `;
+}
+
+export class DurationDisplayBase extends HTMLElement {
+ static shadowRootOptions = {
+ mode: 'open' as ShadowRootMode,
+ };
+ static getTemplateHTML = getTemplateHTML;
+
+ _state:
+ | {
+ duration: number | undefined;
+ isValidDuration: boolean;
+ formattedDuration: string;
+ durationPhrase: string;
+ }
+ | undefined;
+
+ constructor() {
+ super();
+
+ if (!this.shadowRoot) {
+ this.attachShadow(
+ (this.constructor as typeof DurationDisplayBase).shadowRootOptions,
+ );
+
+ const attrs = namedNodeMapToObject(this.attributes);
+ const html = (
+ this.constructor as typeof DurationDisplayBase
+ ).getTemplateHTML(attrs);
+ const shadowRoot = this.shadowRoot as unknown as ShadowRoot;
+ shadowRoot.setHTMLUnsafe
+ ? shadowRoot.setHTMLUnsafe(html)
+ : (shadowRoot.innerHTML = html);
+ }
+ }
+
+ get duration() {
+ return this._state?.duration;
+ }
+
+ get formattedDuration() {
+ return this._state?.formattedDuration || '--:--';
+ }
+
+ get durationPhrase() {
+ return this._state?.durationPhrase || 'Duration unknown';
+ }
+
+ _update(_props: any, state: any) {
+ this._state = state;
+
+ // Update the span content
+ const spanElement = this.shadowRoot?.querySelector('span') as HTMLElement;
+ if (spanElement) {
+ spanElement.textContent = state.formattedDuration;
+ }
+ }
+}
+
+/**
+ * DurationDisplay state hook - equivalent to React's useDurationDisplayState
+ * Handles media store state subscription and transformation
+ */
+export const useDurationDisplayState: StateHook<{
+ duration: number | undefined;
+ isValidDuration: boolean;
+ formattedDuration: string;
+ durationPhrase: string;
+}> = {
+ keys: [...durationDisplayStateDefinition.keys],
+ transform: (rawState, _mediaStore) => ({
+ ...durationDisplayStateDefinition.stateTransform(rawState),
+ // Duration display is read-only, so no request methods needed
+ }),
+};
+
+/**
+ * DurationDisplay props hook - equivalent to React's useDurationDisplayProps
+ * Handles element attributes and properties based on state
+ */
+export const useDurationDisplayProps: PropsHook<{
+ duration: number | undefined;
+ isValidDuration: boolean;
+ formattedDuration: string;
+ durationPhrase: string;
+}> = (_state, _element) => {
+ const baseProps: Record = {};
+ return baseProps;
+};
+
+/**
+ * Connected DurationDisplay component using hook-style architecture
+ * Equivalent to React's DurationDisplay = toConnectedComponent(...)
+ */
+export const DurationDisplay = toConnectedHTMLComponent(
+ DurationDisplayBase,
+ useDurationDisplayState,
+ useDurationDisplayProps,
+ 'DurationDisplay',
+);
+
+// Register the custom element
+if (!globalThis.customElements.get('media-duration-display')) {
+ // @ts-ignore - Custom element constructor compatibility
+ globalThis.customElements.define('media-duration-display', DurationDisplay);
+}
+
+export default DurationDisplay;
\ No newline at end of file
diff --git a/packages/html/html/src/index.ts b/packages/html/html/src/index.ts
index 34ea94ba..7096366a 100644
--- a/packages/html/html/src/index.ts
+++ b/packages/html/html/src/index.ts
@@ -6,6 +6,7 @@ export { PlayButton } from './components/media-play-button.js';
export { MuteButton } from './components/media-mute-button.js';
export { VolumeRange } from './components/media-volume-range.js';
export { FullscreenButton } from './components/media-fullscreen-button.js';
+export { DurationDisplay } from './components/media-duration-display.js';
export function defineVjsPlayer() {
/** @TODO - Reimplement me (at least as a POC) (CJP) */