BETA

Installation

Install the Harpy CLI or integrate Harpy.js manually into an existing project. This page covers system requirements, quick start using the CLI, manual installation, detailed setup steps, and the recommended project structure.

Quick tip: The CLI is the fastest path to a working project. Use manual install only if you need to integrate Harpy.js into an existing codebase.

System Requirements

Minimum

  • Node.js: 18+ (20.x recommended for latest features) — native ESM & modern runtime APIs
  • Package manager: pnpm (recommended), npm, or yarn
  • Git installed

Installation Methods

Choose the method that fits your workflow. The CLI scaffolds a complete project; manual installation is for integrating into existing projects.

Quick Start (recommended)

Use the Harpy CLI to create a new project with sensible defaults and a working dev server.

›_ Terminal
pnpm i -g @hepta-solutions/harpy-cli
harpy create my-app
cd my-app
pnpm dev

The CLI will scaffold routes, layouts, a working dev server and example pages so you can focus on building features.

Manual Installation

Install the core packages and configure the rendering engine if you prefer to add Harpy.js to an existing project.

›_ Terminal
pnpm add @hepta-solutions/harpy-core react react-dom

You'll then set up a layout, configure rendering in your server entry, and add decorators to your controllers (if using NestJS or similar) to render JSX pages.

Why JSX/layouts? Harpy.js uses JSX-based layouts to enable full server-side rendering plus client hydration — layouts are components that wrap pages. Ensure your environment supports JSX tooling.

Detailed Steps

1. Install Dependencies

Add the core runtime and React (latest version recommended) to your project.


pnpm add @hepta-solutions/harpy-core react react-dom
Tip: Use pnpm for faster installs and consistent lockfiles, but npm and yarn are also supported.

2. Create a Layout Component

Layouts render the common shell around pages (head tags, header, footer, navigation) and receive metadata and hydration script definitions.

// layouts/layout.tsx
import * as React from 'react';
import { JsxLayoutProps } from '@hepta-solutions/harpy-core';

export default function MainLayout({ children, meta, hydrationScripts, sections, lang }: JsxLayoutProps & { lang?: string }) {
  const title = meta?.title ?? 'Harpy Framework';
  const description = meta?.description;
  const canonical = meta?.canonical;
  const og = meta?.openGraph ?? {};
  const twitter = meta?.twitter ?? {};
  const chunkScripts = hydrationScripts || [];
  
  return (
    <html lang={lang || 'en'}>
      <head>
        <title>{title}</title>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="stylesheet" href="/styles/styles.css" /> // 👈 Makes sure to add this for styling
        <meta name="description" content={description} />
        <link rel="canonical" href={canonical} />
        {/* Open Graph tags */}
        <meta property="og:title" content={og.title || title} />
        <meta
          property="og:description"
          content={og.description || description}
        />
        <meta property="og:type" content={og.type || 'website'} />
        {og.image && <meta property="og:image" content={og.image} />}
        {og.url && <meta property="og:url" content={og.url} />}

        {/* Twitter cards */}
        <meta
          name="twitter:card"
          content={twitter.card || 'summary_large_image'}
        />
        <meta name="twitter:title" content={twitter.title || title} />
        <meta
          name="twitter:description"
          content={twitter.description || description}
        />
        {twitter.image && <meta name="twitter:image" content={twitter.image} />}
        <link rel="stylesheet" href="/styles/styles.css" />
      </head>
      <body>
        <aside>
          <nav className="px-6 py-6 space-y-8">
            {sections.map((section) => (
              <div key={section.id}>
                <h3 >
                  {section.title}
                </h3>
                <ul className="space-y-1">
                  {section.items.map((item) => (
                    <li key={item.id}>
                      <a href={item.href} >
                        {item.title}
                      </a>
                    </li>
                  ))}
                </ul>
              </div>
            ))}
          </nav>
        </aside>
        <main>
        {children}
        <main>
        {/* Auto-injected hydration scripts */}
        {chunkScripts.map((script) => (
          <script key={script.componentName} src={script.path}></script>
        ))}
      </body>
    </html>
  );
}
Note: the layout receives `sections` to build dynamic navigation and `hydrationScripts` to load client chunks.

3. Configure the Server Entry

Register the JSX engine and static serving in your server bootstrap. Example below shows the renderer being wired into an Fastify server.

import { NestFactory } from '@nestjs/core';
  import { AppModule } from './app.module';
  import { withJsxEngine } from '@hepta-solutions/harpy-core';
  import fastifyStatic from '@fastify/static';
  import {
    FastifyAdapter,
    NestFastifyApplication,
  } from '@nestjs/platform-fastify';
   import DefaultLayout from './layouts/layout';


  async function bootstrap() {
    const app = await NestFactory.create<NestFastifyApplication>(
      AppModule,
      new FastifyAdapter(),
    );

    // Set up JSX rendering engine
    withJsxEngine(app, DefaultLayout);

    // Register Fastify plugins
    const fastify = app.getHttpAdapter().getInstance();

    // Register static file serving
    await fastify.register(fastifyStatic, {
      root: path.join(process.cwd(), 'dist'),
      prefix: '/',
      decorateReply: false,
    });

    await app.listen(3000);
  }
  bootstrap();
Critical:Harpy.js will not work unless you use FastifyAdapter, the JSX rendering engine specifically designed for Fastify.

4. Add Decorators / Render Hooks

If you use a controller-based server (NestJS or similar), use the provided decorator to render JSX pages from route handlers. Otherwise, call the renderer directly from your route handlers.

// controller example (conceptual)
@Controller()
  export class HomeController {
    constructor(private readonly navigationService: NavigationService) {}
  
    @Get()
    @JsxRender(Page, {
      layout: CustomLayout, // 👈 Provide a specific layout here, otherwise DefaultLayout will be used
      meta: {
        title: 'your page title',
        description:
          'your page description',
        canonical: 'https://your-site.com/your-page',
        openGraph: {
          title: 'OPen graph title',
          description:
            'Open graph description',
          type: 'website',
          url: 'https://your-site.com/your-page',
        },
        // Other meta data...
      },
    })
    async home(@CurrentLocale() locale: string): Promise<PageProps> {
      const dict = await getDictionary(locale); // 👈 Load translations
      const sections = this.navigationService.getAllSections(); // 👈 Load navigation sections
  
      return {
        sections,
        dict,
        locale,
      };
    }
  }
Result: Pages render on the server with proper head tags and can hydrate on the client.
Note: Check the internationalization and routing docs for more details.

5. Start the Dev Server

Run the dev server and open your app in the browser:

pnpm dev
# or
npm run dev

The dev server typically includes hot reload for server code and fast client-side updates for components.

Recommended Project Structure

This layout-agnostic structure helps the Harpy rendering engine discover pages, layouts and static assets.

my-app/
├─ src/
│  ├─ assets/        # static assets (imust include styles.css)
│  ├─ components/        # reusable UI components
│  ├─ layouts/        # layout components (main, dashboard, etc.)
│  ├─ dictionaries/        # translation dictionaries
│  ├─ features/          # feature modules (auth, dashboard, etc.)
│  ├─ i18n/          # internationalization config and utilities
│  ├─ shared/          # shared module (must include navigation module)
│  ├─ app.module.ts      # root module
│  ├─ main.ts      # server bootstrap
├─ public/            # static assets
├─ package.json
└─ tailwind.config.ts    # framework configuration (Tailwind CSS)
  • src/assets — Contains static assets such as images, fonts, and required global files like styles.css.
  • src/components — Reusable UI components shared across the application.
  • src/layouts — Layout components (e.g., main layout, dashboard layout) that wrap feature pages.
  • src/dictionaries — Translation dictionaries used for multi-language support.
  • src/features — Feature-based modules (auth, dashboard, admin, etc.), each grouping pages, hooks, and logic.
  • src/i18n — Internationalization configuration and utilities.
  • src/shared — Shared module containing utilities, adapters, and the required navigation module.
  • src/app.module.ts — The root application module.
  • src/main.ts — The main server bootstrap, typically registering the Fastify adapter and SSR engine.
  • public/ — Static public assets.
  • tailwind.config.ts — Tailwind CSS framework configuration.