The official website for Apache HBase, built with modern web technologies to provide a fast, accessible, and maintainable web presence.
Most landing pages store content in Markdown (.md) or JSON (.json) files located in app/pages/_landing/[page-name]/. Docs content lives under app/pages/_docs/ and is authored in MDX.
Legacy documentation is preserved for those users who have old bookmarked links and notes: the old book lives at /public/book.html, and its static assets are in public/old-book-static-files/.
Examples:
app/pages/_landing/team/content.md - Markdown content for team pageapp/pages/_landing/powered-by-hbase/companies.json - JSON data for companiesapp/pages/_landing/news/events.json - JSON data for news/eventsapp/pages/_docs/docs/_mdx/(multi-page)/... - MDX content for documentationBefore you begin, ensure you have the following installed:
Node.js version 22 - JavaScript runtime (like the JVM for Java)
node --version (should show v20.19+ or v22.12+)NPM - Node Package Manager (like Maven for Java)
npm --versionThis website uses modern web technologies. Here's what each one does (with Java analogies):
The website uses progressive enhancement (learn more), which means:
With JavaScript enabled: Users get a Single Page Application (SPA) experience
Without JavaScript: Users still get a fully functional website
This approach ensures the website works for all users, regardless of their browser capabilities or connection speed.
className="text-blue-500 font-bold" makes blue, bold textTypeScript - Typed superset of JavaScript
ESLint + Prettier - Code linting and formatting (like Checkstyle)
npm run lint:fix handles both linting and formattingeslint.config.js and prettier.config.jsThe project follows a clear directory structure with separation of concerns:
my-react-router-app/ ├── app/ # Application source code │ ├── ui/ # Reusable UI components (no business logic) │ │ ├── button.tsx # Generic button component │ │ ├── card.tsx # Card container component │ │ └── ... # Other UI primitives │ │ │ ├── components/ # Reusable components WITH business logic │ │ ├── site-navbar.tsx # Website navigation bar │ │ ├── site-footer.tsx # Website footer │ │ ├── theme-toggle.tsx # Dark/light mode toggle │ │ └── markdown-layout.tsx # Layout for markdown content pages │ │ │ ├── pages/ # Complete pages (composed of ui + components) │ │ ├── _landing/ # Landing pages + layout │ │ │ ├── home/ # Home page │ │ │ │ ├── index.tsx # Main page component (exported) │ │ │ │ ├── hero.tsx # Hero section (not exported) │ │ │ │ ├── features.tsx # Features section (not exported) │ │ │ │ └── ... │ │ │ ├── team/ # Landing page content │ │ │ └── ... │ │ ├── _docs/ # Documentation (Fumadocs) │ │ │ ├── docs/ # MDX content and structure │ │ │ ├── docs-layout.tsx # Fumadocs layout wrapper │ │ │ └── ... │ │ │ ├── routes/ # Route definitions and metadata │ │ ├── home.tsx # Home route configuration │ │ ├── team.tsx # Team route configuration │ │ └── ... │ │ │ ├── lib/ # Utility functions and integrations │ │ ├── utils.ts # Helper functions │ │ └── theme-provider.tsx # Theme management │ │ │ ├── routes.ts # Main routing configuration │ ├── root.tsx # Root layout component │ └── app.css # Global styles │ ├── build/ # Generated files (DO NOT EDIT) │ ├── client/ # Browser-side assets │ │ ├── index.html # HTML files for each page │ │ ├── assets/ # JavaScript, CSS bundles │ │ └── images/ # Optimized images │ ├── public/ # Static files (copied as-is to build/) │ ├── favicon.ico # Website icon │ ├── images/ # Image assets | └── ... │ ├── node_modules/ # Dependencies (like Maven's .m2 directory) ├── package.json # Project metadata and dependencies (like pom.xml) ├── tsconfig.json # TypeScript configuration └── react-router.config.ts # React Router framework configuration
UI Components (/ui): Pure, reusable components with no business logic
Business Components (/components): Reusable across pages
Pages (/pages): Complete pages combining ui and components
index.tsx is exported/componentsRoutes (/routes): Define routing and metadata
Two Layout Systems in One App:
app/pages/_landing/ and use the landing layout.app/pages/_docs/ and use Fumadocs layouts.Documentation Versions:
app/pages/_docs/docs/_mdx/(multi-page)/ and are the source of truth.app/pages/_docs/docs/_mdx/single-page/ and import content from the multi-page docs.Always use the custom Link component from @/components/link instead of importing Link directly from react-router.
The HBase website includes pages that are not part of this React Router application (e.g., documentation pages, API docs). The custom Link component automatically determines whether a link should trigger a hard reload or use React Router's client-side navigation:
Usage:
// ✅ CORRECT - Use custom Link component import { Link } from "@/components/link"; export const MyComponent = () => ( <Link to="/team">Team</Link> );
// ❌ WRONG - Do not import Link from react-router import { Link } from "react-router"; export const MyComponent = () => ( <Link to="/team">Team</Link> );
The ESLint configuration includes a custom rule (custom/no-react-router-link) that will throw an error if you attempt to import Link from react-router, helping enforce this convention automatically.
Think of this as mvn install:
npm install
This downloads all required packages from npm (similar to Maven Central).
Important: Before starting the development server, generate the developers.json file from the root pom.xml:
npm run extract-developers
This extracts the developer information from the parent pom.xml file and creates app/pages/team/developers.json, which is required for the Team page to work properly. Re-run this command whenever the developers section in pom.xml changes. The output json is ignored by git, and this command also runs at a build time, so there is no need to git commit the generated file.
Important: Generate the HBase configuration markdown before starting the development server:
npm run extract-hbase-config
This extracts data from hbase-default.xml and creates app/pages/_docs/docs/_mdx/(multi-page)/configuration/hbase-default.md, which is required for the documentation page to work properly. Re-run this command whenever hbase-default.xml changes. The generated markdown is ignored by git, and this command also runs at build time, so there is no need to git commit the generated file.
Important: Generate the HBase version metadata before starting the development server:
npm run extract-hbase-version
This extracts the <revision> value from the root pom.xml and creates app/lib/export-pdf/hbase-version.json, which is used on the docs PDF cover. Re-run this command whenever the root pom.xml version changes. The generated json is ignored by git, and this command also runs at build time, so there is no need to git commit the generated file.
npm run dev
This starts a local development server with:
http://localhost:5173app/ directorynpm run dev is running and in browser consoleAdd a new page:
app/pages/my-new-page/index.tsx in that directoryapp/routes/my-new-page.tsxapp/routes.tsAdd a new documentation page:
.mdx file in app/pages/_docs/docs/_mdx/(multi-page)/ (for example my-topic.mdx).meta.json in the same section folder so it appears in navigation.app/pages/_docs/docs/_mdx/single-page/index.mdx and add an # header so it renders in the single-page docs.Important: All heading IDs and all footnote reference IDs must be unique across every multi-page MDX file. This is because all files are combined into a single page (see
single-page/index.mdx), so any duplicate ID would collide in the combined document. There are automated unit tests that enforce both constraints —unit-tests/validate-headings.test.tsfor heading IDs andunit-tests/validate-references.test.tsfor footnote reference IDs.
- Headings: Use an explicit
[#page-specific-id]anchor suffix on any heading whose auto-generated slug would clash with a heading in another file. For example:## Overview [#my-topic-overview].- Footnotes: Use page-specific numeric or named identifiers so they remain unique globally. For example, instead of
[^1]in every file, simply continue the global numbering (e.g. if existing files already use[^1]–[^5], start at[^6]in a new file).
Update content:
.md or .json fileAdd a UI component:
Check code quality:
npm run lint
Fix linting and formatting issues:
npm run lint:fix
The project uses Vitest and Playwright for testing. Vitest is for unit testing, while Playwright is for e2e testing.
The docs PDF export is implemented as a Playwright e2e test in e2e-tests/export-pdf.spec.ts. It runs during npm run ci-skip-tests and generates static PDF assets for the documentation by rendering the single-page docs in both light and dark themes (HTML -> PDF).
The export quality depends heavily on the @media print styles defined in app/app.css, which control layout, pagination, and print-only behavior.
There is also a dedicated command you can run manually when needed:
npm run export-pdf
This command is part of npm run ci-skip-tests and is executed automatically by Maven only when -DskipTests is enabled.
Run tests:
# Run all tests npm test # Run unit tests once (for CI/CD) npm run test:run # Run unit tests with UI npm run test:ui # Run e2e tests npm run test:e2e # Run e2e tests with UI npm run test:e2e:ui
Writing new tests:
Use the renderWithProviders utility in test/utils.tsx to ensure components have access to routing and theme context:
import { renderWithProviders, screen } from './utils' import { MyComponent } from '@/components/my-component' describe('MyComponent', () => { it('renders correctly', () => { renderWithProviders(<MyComponent />) expect(screen.getByText('Hello World')).toBeInTheDocument() }) })
CI/CD Workflow:
Before merging or deploying, run the full CI pipeline:
npm run ci
This command runs all quality checks and builds the project. All checks must pass before code is considered ready.
If you need the CI flow without unit/e2e test suites, use:
npm run ci-skip-tests
This runs extraction, docs initialization, Playwright browser installation, PDF export, and the production build (without lint/typecheck and without unit/e2e test suites).
Generated files are located under the build/ directory.
The website is integrated with the Apache HBase Maven build system using the frontend-maven-plugin. The website is configured to build only during site generation (mvn site) and will not build during regular Maven lifecycle phases like mvn clean install.
The website build is triggered only when you run:
mvn site
The website will NOT build during regular commands like:
mvn clean installmvn packagemvn compileThis keeps regular HBase builds fast while still allowing the website to be generated when needed.
mvn siteWhen you run mvn site, the website module automatically:
Cleans previous build artifacts
build/ directorynode_modules/ directoryInstalls Node.js v22.20.0 and npm 11.6.2 (if not already available)
target/ directoryRuns npm install to install all dependencies
package.jsonnode_modules/Extracts developers data from the parent pom.xml
app/pages/team/developers.jsonRuns a website CI command:
mvn site): npm run cimvn site -DskipTests): npm run ci-skip-testsnpm run ci executes:
npm run lint - ESLint code quality checksnpm run typecheck - TypeScript type checkingnpm run extract-developers - Extract developers from parent pom.xmlnpm run extract-hbase-config - Extract data from hbase-default.xml to app/pages/_docs/docs/_mdx/(multi-page)/configuration/hbase-default.mdnpm run extract-hbase-version - Extract version from root pom.xml to app/lib/export-pdf/hbase-version.jsonnpm run test:unit:run - Vitest unit testsnpm run test:e2e - Playwright e2e testsnpm run build - Production buildnpm run ci-skip-tests executes:
npm run extract-developers - Extract developers from parent pom.xmlnpm run extract-hbase-config - Extract data from hbase-default.xml to app/pages/_docs/docs/_mdx/(multi-page)/configuration/hbase-default.mdnpm run extract-hbase-version - Extract version from root pom.xml to app/lib/export-pdf/hbase-version.jsonnpx playwright install - Installs Playwright browsersnpm run export-pdf - Generates docs PDF assets through Playwrightnpm run build - Production buildBuild Output: Generated files are in build/ directory
Build HBase WITHOUT the Website (default):
# From HBase root directory mvn clean install
Build the Website:
# From HBase root or hbase-website directory mvn site
This generates the full HBase website including documentation and the React-based website.
Build the Website While Skipping Test Suites:
# From HBase root or hbase-website directory mvn site -DskipTests
This runs npm run ci-skip-tests for the website module.
Build Website Only:
# From hbase-website directory cd hbase-website mvn clean install
Skip Website Build:
If you want to build HBase but skip the website:
# From HBase root directory mvn clean install -DskipSite
Since this site uses Static Site Generation (SSG), you can deploy the build/client/ directory to any static file host:
build/client/ contents to your web rootbuild/client/ contents to your web rootbuild/client/ to gh-pages branchIf you see type errors related to React Router's +types, regenerate them:
npx react-router typegen
If npm run dev fails because port 5173 is in use:
# Kill the process using the port lsof -ti:5173 | xargs kill -9 # Or change the port in vite.config.ts
Clear generated files:
rm -rf build/ node_modules/ .vite/ .react-router/ .source/
Reinstall dependencies:
npm i
Try building again:
npm run build
Built with ❤️ for the Apache HBase community.