Skip to content

Getting Started

Get up and running with MIDDAG React UI in under five minutes.

Quick Start (recommended)

Run the wizard from your plugin project root:

bash
npx create-middag-ui

The wizard:

  1. Detects your host — finds version.php (Moodle) or wp-config.php (WordPress)
  2. Asks for target directory — default ui/, or pass as argument: npx create-middag-ui mydir
  3. Configures registry — npm public (no auth) or GitHub Packages (with source, needs token)
  4. Scaffolds config + demo files — package.json, tsconfig, Vite config, custom block example, standalone component
  5. Runs npm install — automatically installs all dependencies
  6. Shows next steps — how to start the dev server and integrate with your platform

After the wizard completes:

bash
cd ui
npm run dev:mock    # Opens at http://localhost:5174

You should see a working admin page with a "Setup Complete" metric card and an example data table. That's your first PageContract rendering.

What's next after the wizard?

The generated mock/hello-contract.ts is your starting point. Edit it to match your plugin's real pages, then connect to your backend. See CLI docs for all available commands.

Manual Install

If you prefer to set things up yourself instead of using the wizard:

Option A: npm public registry (no auth needed)

bash
# Install the compiled package directly from npm
npm install @middag-io/react react react-dom @inertiajs/react @inertiajs/core

Option B: GitHub Packages (includes TypeScript source)

bash
# Configure GitHub Packages in your global ~/.npmrc
echo "@middag-io:registry=https://npm.pkg.github.com" >> ~/.npmrc
echo "//npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKEN" >> ~/.npmrc

# Install the package with full source
npm install @middag-io/react react react-dom @inertiajs/react @inertiajs/core

Create a token at github.com/settings/tokens with the read:packages scope. See Authentication for details.

Which option?

npm public gives you the compiled library and type definitions. Works for most projects. GitHub Packages additionally includes the TypeScript source for IDE navigation into library internals. Requires a GitHub token with read:packages scope.

Peer dependencies

react >= 19, react-dom >= 19, @inertiajs/react >= 2, and @inertiajs/core >= 2 are required. If you're already using Inertia.js, these are included.

Import & Register

Call registerDefaults() once at your app entry point to populate the shell, layout, and block registries with the built-in components:

tsx
// main.tsx
import { registerDefaults } from '@middag-io/react';
import '@middag-io/react/style.css';

// Register all built-in shells, layouts, and blocks
registerDefaults();

Wrap with Providers

Wrap your app root with the required providers:

tsx
// App.tsx
import { I18nProvider } from '@middag-io/react';

function App({ children }) {
    return (
        <I18nProvider
            locale="en"
            resolve={(key) => translations[key] ?? key}
        >
            {children}
        </I18nProvider>
    );
}

Inertia integration

When using Inertia.js, the providers are typically set up in your app.tsx boot file. The shared props from the backend automatically feed navigation, auth, and flash data.

Render ContractPage

Pass a PageContract object to ContractPage and it handles the rest -- resolving the shell, layout, and blocks from the registries:

tsx
// MyPage.tsx
import { ContractPage } from '@middag-io/react';
import type { PageContract } from '@middag-io/react';

const contract: PageContract = {
    version: '1',
    shell: 'product',
    page: {
        key: 'users',
        title: 'Users',
        breadcrumbs: [
            { label: 'Home', href: '/' },
            { label: 'Users' },
        ],
    },
    layout: {
        template: 'stack',
        regions: {
            content: [
                {
                    type: 'dense_table',
                    key: 'users-table',
                    data: {
                        columns: [
                            { key: 'name', label: 'Name' },
                            { key: 'email', label: 'Email' },
                        ],
                        rows: [
                            { id: '1', cells: { name: 'Alice', email: '[email protected]' } },
                        ],
                    },
                },
            ],
        },
    },
};

export default function UsersPage() {
    return <ContractPage contract={contract} />;
}

Send Contract from Backend

In Moodle, use the Inertia adapter to send the contract as page props:

php
// PHP -- Moodle Controller
return $this->inertia->render('ContractPage', [
    'contract' => [
        'version' => '1',
        'shell'   => 'product',
        'page'    => [
            'key'         => 'users',
            'title'       => get_string('users', 'local_myplugin'),
            'breadcrumbs' => [
                ['label' => 'Home', 'href' => '/'],
                ['label' => get_string('users', 'local_myplugin')],
            ],
        ],
        'layout' => [
            'template' => 'stack',
            'regions'  => [
                'content' => [
                    [
                        'type' => 'dense_table',
                        'key'  => 'users-table',
                        'data' => $table_data,
                    ],
                ],
            ],
        ],
    ],
]);

WordPress

The same contract shape works with WordPress through the host adapter system. The backend just needs to output the PageContract JSON.

What's Next

Now that you have a working setup, explore the rest of the documentation:

Released under the proprietary license.