Skip to content

Frontend Quick Start

This guide shows how to integrate granit-front into an existing Vite/React application. By the end, the application will have a structured logger, an authenticated HTTP client, and a typed Keycloak authentication context.

flowchart LR
    APP[Vite/React App]
    APP --> AUTH["@granit/react-authentication"]
    APP --> API["@granit/api-client"]
    APP --> UTILS["@granit/utils"]
    APP --> LOGGER["@granit/logger"]
    AUTH --> API
  • Node.js 24+
  • pnpm 10+
  • A running Keycloak instance (or any OIDC provider)
  • Familiarity with TypeScript and React
  1. Clone the repository

    Clone granit-front next to the consuming application:

    workspace/
    ├── granit-front/ ← framework monorepo
    └── my-app/ ← your Vite/React application
    Terminal window
    cd workspace
    git clone <repository-url> granit-front
    cd granit-front && pnpm install
  2. Declare dependencies

    In the application’s package.json, add packages using the link: protocol:

    {
    "dependencies": {
    "@granit/logger": "link:../granit-front/packages/@granit/logger",
    "@granit/utils": "link:../granit-front/packages/@granit/utils",
    "@granit/api-client": "link:../granit-front/packages/@granit/api-client",
    "@granit/react-authentication": "link:../granit-front/packages/@granit/react-authentication"
    }
    }

    Then install peer dependencies:

    Terminal window
    pnpm add axios clsx tailwind-merge date-fns keycloak-js
  3. Configure Vite and TypeScript

    Add aliases so Vite resolves TypeScript sources directly (source-direct pattern):

    import path from 'path';
    import { defineConfig } from 'vite';
    const GRANIT = path.resolve(__dirname, '../granit-front/packages/@granit');
    export default defineConfig({
    resolve: {
    alias: {
    '@granit/logger': path.join(GRANIT, 'logger/src/index.ts'),
    '@granit/utils': path.join(GRANIT, 'utils/src/index.ts'),
    '@granit/api-client': path.join(GRANIT, 'api-client/src/index.ts'),
    '@granit/react-authentication': path.join(GRANIT, 'react-authentication/src/index.ts'),
    },
    },
    });
  4. Create the logger and API client

    import { createLogger } from '@granit/logger';
    export const logger = createLogger('[MyApp]');
  5. Configure Keycloak authentication

    Create the authentication context and provider:

    src/auth/auth-context.ts
    import { createAuthContext } from '@granit/react-authentication';
    import type { BaseAuthContextType } from '@granit/authentication';
    interface AuthContextType extends BaseAuthContextType {
    // Add application-specific fields if needed
    }
    export const { AuthContext, useAuth } = createAuthContext<AuthContextType>();
    src/auth/AuthProvider.tsx
    import { useKeycloakInit } from '@granit/react-authentication';
    import { AuthContext } from './auth-context';
    export function AuthProvider({ children }: { children: React.ReactNode }) {
    const auth = useKeycloakInit({
    url: import.meta.env.VITE_KEYCLOAK_URL,
    realm: import.meta.env.VITE_KEYCLOAK_REALM,
    clientId: import.meta.env.VITE_KEYCLOAK_CLIENT_ID,
    });
    if (auth.loading) return <div>Loading…</div>;
    return (
    <AuthContext.Provider value={auth}>
    {children}
    </AuthContext.Provider>
    );
    }

    Wrap the application:

    src/main.tsx
    import { AuthProvider } from './auth/AuthProvider';
    import { App } from './App';
    createRoot(document.getElementById('root')!).render(
    <AuthProvider>
    <App />
    </AuthProvider>
    );
Terminal window
# Verify granit-front is healthy
cd ../granit-front
pnpm lint && pnpm tsc && pnpm test run
# Start the application
cd ../my-app
pnpm dev
import { useAuth } from './auth/auth-context';
import { logger } from '@/lib/logger';
function UserProfile() {
const { user, logout } = useAuth();
logger.info('Profile displayed', { userId: user?.sub });
return (
<div>
<p>{user?.name}</p>
<button onClick={logout}>Sign out</button>
</div>
);
}
import { useQueryEndpoint } from '@granit/querying'
→ Vite alias → packages/@granit/querying/src/index.ts
→ TypeScript source → transpiled on the fly by Vite
→ no dist/, no intermediate build

This source-direct approach provides instant HMR on framework code, zero build watch overhead, direct source maps, and frictionless refactoring across framework and application boundaries.