# Examples

Each example demonstrates the recommended project structure and patterns using TanStack Router with file-based routing.

### <mark style="color:red;">Fullpage Application</mark>

A fullpage application takes over the entire content area of the shell and has its own menu.

#### Project Structure

```
src/
├── components/
│   └── Loading.tsx
├── hooks/
│   ├── index.tsx
│   └── useConfig.tsx
├── routes/
│   ├── __root.tsx
│   ├── index.tsx
│   └── about.tsx
├── AppWrapper.tsx
├── config.ts
├── main.tsx
├── providers.tsx
└── routeTree.gen.ts
```

#### Entry Point (main.tsx)

```tsx
import ReactDOM from 'react-dom/client';
import { RouterProvider, createRouter } from '@tanstack/react-router';
import '@akinon/fonts-jost-variable';
import '@akinon/ui-utils';

import { routeTree } from './routeTree.gen';

import './main.scss';

const router = createRouter({ routeTree });

declare module '@tanstack/react-router' {
  interface Register {
    router: typeof router;
  }
}

ReactDOM.createRoot(document.getElementById('root')!).render(
  <RouterProvider router={router} />
);
```

#### Root Route (routes/\_\_root.tsx)

```tsx
import { Outlet, createRootRoute } from '@tanstack/react-router';
import { Suspense } from 'react';

import { Providers } from '@/providers';
import { Loading } from '@/components/Loading';
import { AppWrapper } from '@/AppWrapper';

export const Route = createRootRoute({
  component: () => (
    <Suspense fallback={<Loading />}>
      <Providers>
        <AppWrapper>
          <Outlet />
        </AppWrapper>
      </Providers>
    </Suspense>
  ),
});
```

#### Home Route (routes/index.tsx)

```tsx
import { createFileRoute } from '@tanstack/react-router';
import { Layout, PageHeading, PageContent } from '@akinon/ui-layout';
import { Breadcrumb } from '@akinon/ui-breadcrumb';
import { useTranslation } from '@akinon/akilocale/react';

export const Route = createFileRoute('/')({
  component: PageHome,
});

function PageHome() {
  const { t } = useTranslation();

  return (
    <Layout>
      <Breadcrumb items={[{ title: t('home.page') }]} />
      <Layout.Content>
        <div className="page-heading-container">
          <PageHeading title={t('home.page')} />
        </div>
        <div className="page-content-container">
          <PageContent>{t('hello.world')}</PageContent>
        </div>
      </Layout.Content>
    </Layout>
  );
}
```

#### About Route (routes/about.tsx)

```tsx
import { createFileRoute } from '@tanstack/react-router';
import { Layout, PageHeading, PageContent } from '@akinon/ui-layout';
import { Breadcrumb } from '@akinon/ui-breadcrumb';
import { useTranslation } from '@akinon/akilocale/react';

export const Route = createFileRoute('/about')({
  component: PageAbout,
});

function PageAbout() {
  const { t } = useTranslation();

  return (
    <Layout>
      <Breadcrumb items={[{ title: t('about.page') }]} />
      <Layout.Content>
        <div className="page-heading-container">
          <PageHeading title={t('about.page')} />
        </div>
        <div className="page-content-container">
          <PageContent>{t('about.content')}</PageContent>
        </div>
      </Layout.Content>
    </Layout>
  );
}
```

#### Providers (providers.tsx)

```tsx
import { AppClientProvider } from '@akinon/app-client';
import { AkinonUiProvider } from '@akinon/ui-system';
import { useRouter } from '@tanstack/react-router';

import { config as rootConfig } from '@/config';

export const Providers = ({ children }: { children: React.ReactNode }) => {
  const router = useRouter();

  const config = {
    ...rootConfig,
    navigation: {
      navigate: ({ path }: { path: string }) => {
        router.navigate({ to: path });
      },
    },
  };

  return (
    <AkinonUiProvider>
      <AppClientProvider config={config}>
        {children}
      </AppClientProvider>
    </AkinonUiProvider>
  );
};
```

#### Configuration (config.ts)

```typescript
import type { FullpageApplicationConfig } from '@akinon/app-client';
import { i18n } from '@akinon/akilocale/react';

const { t } = i18n;

export const config: Omit<FullpageApplicationConfig, 'navigation'> = {
  isDev: true,
  forceRedirect: true,
  menu: [
    {
      label: t('home.page'),
      path: '/',
    },
    {
      label: t('about.page'),
      path: '/about',
    },
  ],
};
```

#### App Wrapper (AppWrapper.tsx)

Handles locale synchronization with the shell:

```tsx
import { useEffect } from 'react';
import { useAppClient } from '@akinon/app-client';
import { useTranslation } from '@akinon/akilocale/react';
import { akidate } from '@akinon/akidate';

export const AppWrapper = ({
  children,
}: {
  children: React.ReactNode;
}) => {
  const { i18n } = useTranslation();
  const { locale, onLocaleChange } = useAppClient();

  useEffect(() => {
    i18n.changeLanguage(locale);
    akidate.setLocale(locale);

    const unsubscribe = onLocaleChange((newLocale) => {
      i18n.changeLanguage(newLocale);
      akidate.setLocale(newLocale);
    });

    return unsubscribe;
  }, [locale, onLocaleChange, i18n]);

  return children;
};
```

***

### <mark style="color:red;">Plugin Application</mark>

A plugin application embeds into specific areas of the shell using placeholder IDs. Plugin applications don't need routing since they render a single component.

#### Project Structure

```
src/
├── components/
│   ├── Content.tsx
│   └── Loading.tsx
├── App.tsx
├── AppWrapper.tsx
├── config.ts
├── main.tsx
└── providers.tsx
```

#### Entry Point (main.tsx)

```tsx
import ReactDOM from 'react-dom/client';
import '@akinon/fonts-jost-variable';
import '@akinon/ui-utils';

import { App } from '@/App';

import './main.scss';

ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
```

#### Configuration (config.ts)

```typescript
import type { PluginApplicationConfig } from '@akinon/app-client';

export const config: PluginApplicationConfig = {
  isDev: true,
  forceRedirect: true,
  placeholderId: 'product-widget',
};
```

#### Providers (providers.tsx)

```tsx
import { AppClientProvider } from '@akinon/app-client';
import { AkinonUiProvider } from '@akinon/ui-system';

import { config } from '@/config';

export const Providers = ({ children }: { children: React.ReactNode }) => {
  return (
    <AkinonUiProvider>
      <AppClientProvider config={config}>
        {children}
      </AppClientProvider>
    </AkinonUiProvider>
  );
};
```

#### App Component (App.tsx)

```tsx
import { Suspense } from 'react';

import { Providers } from '@/providers';
import { Loading } from '@/components/Loading';
import { Content } from '@/components/Content';
import { AppWrapper } from '@/AppWrapper';

export const App = () => {
  return (
    <Suspense fallback={<Loading />}>
      <Providers>
        <AppWrapper>
          <Content />
        </AppWrapper>
      </Providers>
    </Suspense>
  );
};
```

#### Content Component (components/Content.tsx)

```tsx
import { useAppClient } from '@akinon/app-client';
import { useTranslation } from '@akinon/akilocale/react';

export const Content = () => {
  const { t } = useTranslation();
  const { params, showToast } = useAppClient();

  const handleClick = () => {
    showToast(t('action.success'), 'success');
  };

  return (
    <div>
      <h3>{t('plugin.title')}</h3>
      <p>Product ID: {params?.productId}</p>
      <button onClick={handleClick}>{t('action.button')}</button>
    </div>
  );
};
```

***

### <mark style="color:red;">Multi-Hybrid Application</mark>

A multi-hybrid application serves both fullpage and plugin applications from a single codebase using different base paths.

#### Project Structure

```
src/
├── components/
├── routes/
│   ├── __root.tsx
│   ├── fullpage-app/
│   │   ├── route.tsx
│   │   └── home.tsx
│   └── plugin-app/
│       └── route.tsx
├── AppWrapper.tsx
├── main.tsx
├── providers.tsx
└── routeTree.gen.ts
```

#### Entry Point (main.tsx)

```tsx
import ReactDOM from 'react-dom/client';
import { RouterProvider, createRouter } from '@tanstack/react-router';
import { MultiAppProvider } from '@akinon/app-client';
import '@akinon/fonts-jost-variable';
import '@akinon/ui-utils';

import { routeTree } from './routeTree.gen';
import { useConfig } from '@/hooks';

import './main.scss';

const router = createRouter({ routeTree });

declare module '@tanstack/react-router' {
  interface Register {
    router: typeof router;
  }
}

const Main = () => {
  const { config } = useConfig();

  return (
    <MultiAppProvider multiConfig={config}>
      <RouterProvider router={router} />
    </MultiAppProvider>
  );
};

ReactDOM.createRoot(document.getElementById('root')!).render(<Main />);
```

#### Root Route (routes/\_\_root.tsx)

```tsx
import { Outlet, createRootRoute } from '@tanstack/react-router';
import { Suspense } from 'react';

import { Providers } from '@/providers';
import { Loading } from '@/components/Loading';
import { AppWrapper } from '@/AppWrapper';

export const Route = createRootRoute({
  component: () => (
    <Suspense fallback={<Loading />}>
      <Providers>
        <AppWrapper>
          <Outlet />
        </AppWrapper>
      </Providers>
    </Suspense>
  ),
});
```

#### Fullpage App Layout (routes/fullpage-app/route.tsx)

```tsx
import { Outlet, createFileRoute } from '@tanstack/react-router';

export const Route = createFileRoute('/fullpage-app')({
  component: () => <Outlet />,
});
```

#### Fullpage Home (routes/fullpage-app/home.tsx)

```tsx
import { createFileRoute } from '@tanstack/react-router';
import { Layout, PageHeading, PageContent } from '@akinon/ui-layout';
import { Breadcrumb } from '@akinon/ui-breadcrumb';
import { useTranslation } from '@akinon/akilocale/react';

export const Route = createFileRoute('/fullpage-app/home')({
  component: PageFullpageApp,
});

function PageFullpageApp() {
  const { t } = useTranslation();

  return (
    <Layout>
      <Breadcrumb
        items={[
          { title: t('fullpage.app.in.multi.hybrid') },
          { title: t('home.page') },
        ]}
      />
      <Layout.Content>
        <div className="page-heading-container">
          <PageHeading title={t('fullpage.app.in.multi.hybrid')} />
        </div>
        <div className="page-content-container">
          <PageContent>{t('hello.world')}</PageContent>
        </div>
      </Layout.Content>
    </Layout>
  );
}
```

#### Plugin App Route (routes/plugin-app/route.tsx)

```tsx
import { createFileRoute } from '@tanstack/react-router';
import { Layout, PageHeading, PageContent } from '@akinon/ui-layout';
import { useTranslation } from '@akinon/akilocale/react';

export const Route = createFileRoute('/plugin-app')({
  component: PagePluginApp,
});

function PagePluginApp() {
  const { t } = useTranslation();

  return (
    <Layout>
      <Layout.Content>
        <div className="page-heading-container">
          <PageHeading title={t('plugin.app.in.multi.hybrid')} />
        </div>
        <div className="page-content-container">
          <PageContent>{t('hello.world')}</PageContent>
        </div>
      </Layout.Content>
    </Layout>
  );
}
```

#### Config Hook (hooks/useConfig.tsx)

```tsx
import { useTranslation } from '@akinon/akilocale/react';
import type { MultiAppConfig } from '@akinon/app-client';
import { useRef } from 'react';

export const useConfig = () => {
  const { t } = useTranslation();

  const configRef = useRef<MultiAppConfig>({
    apps: {
      fullpageApp: {
        basePath: '/fullpage-app',
        type: 'full_page',
        config: {
          isDev: true,
          forceRedirect: false,
          menu: [
            {
              label: t('home.page'),
              path: '/home',
            },
          ],
        },
      },
      pluginApp: {
        basePath: '/plugin-app',
        type: 'plugin',
        config: {
          isDev: true,
          forceRedirect: true,
          placeholderId: 'plugin-placeholder',
        },
      },
    },
    defaultApp: 'fullpageApp',
  });

  return { config: configRef.current };
};
```

#### Providers (providers.tsx)

```tsx
import { AkinonUiProvider } from '@akinon/ui-system';

export const Providers = ({ children }: { children: React.ReactNode }) => {
  return <AkinonUiProvider>{children}</AkinonUiProvider>;
};
```

***

### <mark style="color:red;">Common Patterns</mark>

#### Data Access Pattern

```tsx
import { useAppClient } from '@akinon/app-client';

const DataConsumer = () => {
  const { data, isLoading } = useAppClient();

  if (isLoading) {
    return <Skeleton />;
  }

  return (
    <div>
      <p>User: {data?.user?.name}</p>
      <p>Theme: {data?.theme}</p>
    </div>
  );
};
```

#### Action Invocation Pattern

```tsx
import { useAppClient } from '@akinon/app-client';

const ActionHandler = () => {
  const { invokeAction, showToast, showErrorMessage } = useAppClient();

  const handleSubmit = async (formData: FormData) => {
    try {
      const result = await invokeAction('submitForm', formData);
      showToast('Form submitted successfully!', 'success');
      return result;
    } catch (error) {
      showErrorMessage('Submission Failed', error.message);
    }
  };

  return <Form onSubmit={handleSubmit} />;
};
```

#### Navigation Pattern

```tsx
import { useAppClient } from '@akinon/app-client';
import { useState } from 'react';

const NavigationComponent = () => {
  const { navigate, showConfirmationDialog } = useAppClient();
  const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);

  const handleNavigate = (path: string) => {
    if (hasUnsavedChanges) {
      showConfirmationDialog({
        title: 'Unsaved Changes',
        content: 'You have unsaved changes. Leave anyway?',
        onConfirm: () => navigate({ path }),
        onCancel: () => {},
      });
    } else {
      navigate({ path });
    }
  };

  return (
    <nav>
      <button onClick={() => handleNavigate('/dashboard')}>Dashboard</button>
      <button onClick={() => handleNavigate('/settings')}>Settings</button>
    </nav>
  );
};
```

#### Rich Modal Pattern

```tsx
import { useAppClient } from '@akinon/app-client';

// Parent component
const ProductList = () => {
  const { showRichModal } = useAppClient();

  const openDetail = (productId: string) => {
    showRichModal(
      `/product-detail`,
      { productId, mode: 'view' },
      { maxWidth: 800, maxHeight: '90vh' }
    );
  };

  return (
    <ul>
      {products.map(product => (
        <li key={product.id}>
          {product.name}
          <button onClick={() => openDetail(product.id)}>View</button>
        </li>
      ))}
    </ul>
  );
};

// Modal content (separate route)
const ProductDetail = () => {
  const { modalContext } = useAppClient();
  const { productId, mode } = modalContext as { productId: string; mode: string };

  return (
    <div>
      <h1>Product Detail</h1>
      <p>ID: {productId}</p>
      <p>Mode: {mode}</p>
    </div>
  );
};
```

### <mark style="color:red;">Getting Started</mark>

Use the CLI to create a new application:

```bash
pnpm create akinon-app
```

Select your application type when prompted:

* **full\_page** - Fullpage application
* **plugin** - Plugin application
* **multi\_full\_page** - Multiple fullpage applications
* **multi\_plugin** - Multiple plugin applications

### <mark style="color:red;">Related</mark>

* Quick Start - Getting started guide
* Shell Application - Shell documentation
* Client Application - Client documentation


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.akinon.com/akinon-ui/ui-protocol/examples.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
