diff --git a/console-webapp/src/app/domains/domainList.component.scss b/console-webapp/src/app/domains/domainList.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/console-webapp/src/app/domains/domainList.component.spec.ts b/console-webapp/src/app/domains/domainList.component.spec.ts
new file mode 100644
index 000000000..e2a526c8a
--- /dev/null
+++ b/console-webapp/src/app/domains/domainList.component.spec.ts
@@ -0,0 +1,36 @@
+// Copyright 2023 The Nomulus Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DomainListComponent } from './domainList.component';
+
+describe('DomainListComponent', () => {
+ let component: DomainListComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [DomainListComponent],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(DomainListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/console-webapp/src/app/domains/domainList.component.ts b/console-webapp/src/app/domains/domainList.component.ts
new file mode 100644
index 000000000..6c5c754b3
--- /dev/null
+++ b/console-webapp/src/app/domains/domainList.component.ts
@@ -0,0 +1,80 @@
+// Copyright 2023 The Nomulus Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { Component, ViewChild } from '@angular/core';
+import { MatTableDataSource } from '@angular/material/table';
+import { BackendService } from '../shared/services/backend.service';
+import { MatPaginator, PageEvent } from '@angular/material/paginator';
+import { RegistrarService } from '../registrar/registrar.service';
+import { Domain, DomainListService } from './domainList.service';
+
+@Component({
+ selector: 'app-domain-list',
+ templateUrl: './domainList.component.html',
+ styleUrls: ['./domainList.component.scss'],
+ providers: [DomainListService],
+})
+export class DomainListComponent {
+ public static PATH = 'domain-list';
+
+ displayedColumns: string[] = [
+ 'domainName',
+ 'creationTime',
+ 'registrationExpirationTime',
+ 'statuses',
+ ];
+
+ dataSource: MatTableDataSource = new MatTableDataSource();
+ isLoading = true;
+
+ pageNumber?: number;
+ resultsPerPage = 50;
+ totalResults?: number;
+
+ @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator;
+
+ constructor(
+ private backendService: BackendService,
+ private domainListService: DomainListService,
+ private registrarService: RegistrarService
+ ) {}
+
+ ngOnInit() {
+ this.dataSource.paginator = this.paginator;
+ this.reloadData();
+ }
+
+ reloadData() {
+ this.isLoading = true;
+ this.domainListService
+ .retrieveDomains(this.pageNumber, this.resultsPerPage, this.totalResults)
+ .subscribe((domainListResult) => {
+ this.dataSource.data = domainListResult.domains;
+ this.totalResults = domainListResult.totalResults;
+ this.isLoading = false;
+ });
+ }
+
+ /** TODO: the backend will need to accept a filter string. */
+ applyFilter(event: KeyboardEvent) {
+ // const filterValue = (event.target as HTMLInputElement).value;
+ this.reloadData();
+ }
+
+ onPageChange(event: PageEvent) {
+ this.pageNumber = event.pageIndex;
+ this.resultsPerPage = event.pageSize;
+ this.reloadData();
+ }
+}
diff --git a/console-webapp/src/app/domains/domainList.service.ts b/console-webapp/src/app/domains/domainList.service.ts
new file mode 100644
index 000000000..b2a4a6a4f
--- /dev/null
+++ b/console-webapp/src/app/domains/domainList.service.ts
@@ -0,0 +1,66 @@
+// Copyright 2023 The Nomulus Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { Injectable } from '@angular/core';
+import { BackendService } from '../shared/services/backend.service';
+import { RegistrarService } from '../registrar/registrar.service';
+import { tap } from 'rxjs';
+
+export interface CreateAutoTimestamp {
+ creationTime: string;
+}
+
+export interface Domain {
+ creationTime: CreateAutoTimestamp;
+ currentSponsorRegistrarId: string;
+ domainName: string;
+ registrationExpirationTime: string;
+ statuses: string[];
+}
+
+export interface DomainListResult {
+ checkpointTime: string;
+ domains: Domain[];
+ totalResults: number;
+}
+
+@Injectable()
+export class DomainListService {
+ checkpointTime?: string;
+
+ constructor(
+ private backendService: BackendService,
+ private registrarService: RegistrarService
+ ) {}
+
+ retrieveDomains(
+ pageNumber?: number,
+ resultsPerPage?: number,
+ totalResults?: number
+ ) {
+ return this.backendService
+ .getDomains(
+ this.registrarService.activeRegistrarId,
+ this.checkpointTime,
+ pageNumber,
+ resultsPerPage,
+ totalResults
+ )
+ .pipe(
+ tap((domainListResult: DomainListResult) => {
+ this.checkpointTime = domainListResult.checkpointTime;
+ })
+ );
+ }
+}
diff --git a/console-webapp/src/app/home/widgets/domains-widget.component.html b/console-webapp/src/app/home/widgets/domains-widget.component.html
index 7df3b1621..f30dcdf9b 100644
--- a/console-webapp/src/app/home/widgets/domains-widget.component.html
+++ b/console-webapp/src/app/home/widgets/domains-widget.component.html
@@ -13,7 +13,12 @@
Create a Domain