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/downloads/content.md- Markdown content for downloads 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 documentation
Before you begin, ensure you have the following installed:
-
Node.js version 22 - JavaScript runtime (like the JVM for Java)
- Download from nodejs.org
- Verify installation:
node --version(should show v22.12+)
-
NPM - Node Package Manager (like Maven for Java)
- Comes bundled with Node.js
- Verify installation:
npm --version
This website uses modern web technologies. Here's what each one does (with Java analogies):
- React Router - Full-stack web framework with Server-Side Generation (SSG)
- Handles routing (like Spring MVC controllers)
- Provides server-side rendering for better performance and SEO
- Enables progressive enhancement (see below)
- Documentation
- Fumadocs - Documentation framework used for the docs section
- Provides MDX-based docs structure and navigation
- Lives alongside the landing pages in the same React Router app
- Supports multi-page and single-page docs from the same MDX sources
- Documentation
The website uses progressive enhancement (learn more), which means:
-
With JavaScript enabled: Users get a Single Page Application (SPA) experience
- Fast page transitions without full page reloads
- Smooth animations and interactive features
- Enhanced user experience
-
Without JavaScript: Users still get a fully functional website
- All links and forms work via traditional HTML
- Content is accessible to everyone
- Better for search engines and accessibility tools
This approach ensures the website works for all users, regardless of their browser capabilities or connection speed.
- shadcn/ui - Pre-built, accessible UI components
- Similar to a component library like PrimeFaces or Vaadin in Java
- Provides buttons, cards, navigation menus, etc.
- Documentation
- TailwindCSS - Utility-first CSS framework, aka Bootstrap on steroids
- Instead of writing CSS files, you apply classes directly in components
- Example:
className="text-blue-500 font-bold"makes blue, bold text
-
TypeScript - Typed superset of JavaScript
- Similar to Java's type system
- Catches errors at compile-time instead of runtime
- Provides autocomplete and better IDE support
-
ESLint + Prettier - Code linting and formatting (like Checkstyle)
- ESLint analyzes code for potential errors and enforces coding standards
- Prettier handles automatic code formatting (spacing, indentation, etc.)
- Integrated together:
npm run lint:fixhandles both linting and formatting - Configuration:
eslint.config.jsandprettier.config.js
The 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- Can be used anywhere in the application
- Only concerned with appearance and basic interaction
-
Business Components (
/components): Reusable across pages- May contain business logic specific to HBase website
- Examples: navigation, footer, theme toggle
-
Pages (
/pages): Complete pages combining ui and components- Each page has its own directory
- Only
index.tsxis exported - Internal components stay within the page directory
- If a component needs to be reused, move it to
/components
-
Routes (
/routes): Define routing and metadata- Maps URLs to pages
- Sets page titles, meta tags, etc.
-
Two Layout Systems in One App:
- Landing pages live under
app/pages/_landing/and use the landing layout. - Docs pages live under
app/pages/_docs/and use Fumadocs layouts. - Both are part of the same React Router application, but render with different layouts and visual styles.
- Landing pages live under
-
Documentation Versions:
- Multi-page docs live under
app/pages/_docs/docs/_mdx/(multi-page)/and are the source of truth. - Single-page docs live under
app/pages/_docs/docs/_mdx/single-page/and import content from the multi-page docs.
- Multi-page docs live under
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 installThis 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-developersThis extracts the developer information from the parent pom.xml file and creates app/pages/_landing/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-configThis 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-versionThis 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 devThis starts a local development server with:
- Hot Module Replacement (HMR): Code changes appear instantly without full page reload
- Live at:
http://localhost:5173
- Edit code in the
app/directory - Save the file - changes appear automatically in the browser
- Check for errors in the terminal where
npm run devis running and in browser console
Add a new page:
- Create directory in
app/pages/my-new-page/ - Create
index.tsxin that directory - Create route file in
app/routes/my-new-page.tsx - Register route in
app/routes.ts
Add a new documentation page:
- Create a new
.mdxfile inapp/pages/_docs/docs/_mdx/(multi-page)/(for examplemy-topic.mdx). - Add the new file to the relevant
meta.jsonin the same section folder so it appears in navigation. - Import the page into
app/pages/_docs/docs/_mdx/single-page/index.mdxand 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:
- Edit the appropriate
.mdor.jsonfile - Changes appear automatically
Add a UI component:
- Check if shadcn/ui has what you need first
- Only create custom components if necessary
Check code quality:
npm run lintFix linting and formatting issues:
npm run lint:fixThe 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-pdfThis 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:uiWriting new tests:
Use the renderWithProviders utility in unit-tests/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 ciThis 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-testsThis 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 siteThe website will NOT build during regular commands like:
mvn clean installmvn packagemvn compile
This keeps regular HBase builds fast while still allowing the website to be generated when needed.
When you run mvn site, the website module automatically:
-
Cleans previous build artifacts
- Removes
build/directory - Removes
node_modules/directory - Ensures a fresh build environment
- Removes
-
Installs Node.js v22.20.0 and npm 11.6.2 (if not already available)
- Installed to
target/directory - Does not affect your system Node/npm installation
- Installed to
-
Runs
npm installto install all dependencies- Reads from
package.json - Installs to
node_modules/
- Reads from
-
Extracts developers data from the parent
pom.xml- Creates
app/pages/_landing/team/developers.json - Required for the Team page
- Creates
-
Runs a website CI command:
- Default (
mvn site):npm run ci - With test skipping (
mvn site -DskipTests):npm run ci-skip-tests
npm run ciexecutes: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 fromhbase-default.xmltoapp/pages/_docs/docs/_mdx/(multi-page)/configuration/hbase-default.mdnpm run extract-hbase-version- Extract version from rootpom.xmltoapp/lib/export-pdf/hbase-version.jsonnpm run test:unit:run- Vitest unit testsnpm run test:e2e- Playwright e2e testsnpm run build- Production build
npm run ci-skip-testsexecutes:npm run extract-developers- Extract developers from parent pom.xmlnpm run extract-hbase-config- Extract data fromhbase-default.xmltoapp/pages/_docs/docs/_mdx/(multi-page)/configuration/hbase-default.mdnpm run extract-hbase-version- Extract version from rootpom.xmltoapp/lib/export-pdf/hbase-version.jsonnpx playwright install- Installs Playwright browsersnpm run export-pdf- Generates docs PDF assets through Playwrightnpm run build- Production build
- Default (
-
Build Output: Generated files are in
build/directory
Build HBase WITHOUT the Website (default):
# From HBase root directory
mvn clean installBuild the Website:
# From HBase root or hbase-website directory
mvn siteThis 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 -DskipTestsThis runs npm run ci-skip-tests for the website module.
Build Website Only:
# From hbase-website directory
cd hbase-website
mvn clean installSkip Website Build:
If you want to build HBase but skip the website:
# From HBase root directory
mvn clean install -DskipSiteSince this site uses Static Site Generation (SSG), you can deploy the build/client/ directory to any static file host:
- Apache HTTP Server: Copy
build/client/contents to your web root - Nginx: Copy
build/client/contents to your web root - GitHub Pages: Push
build/client/togh-pagesbranch
If you see type errors related to React Router's +types, regenerate them:
npx react-router typegenIf 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
- React Router Documentation: https://reactrouter.com/
- Progressive Enhancement Explained: https://reactrouter.com/explanation/progressive-enhancement
- shadcn/ui Components: https://ui.shadcn.com/
- TailwindCSS Docs: https://tailwindcss.com/
- TypeScript Handbook: https://www.typescriptlang.org/docs/
Built with ❤️ for the Apache HBase community.