Skip to main content
@frontmcp/ui is a platform-aware HTML component kit designed for LLM surfaces. It renders complete markup (no React runtime) and ships a Tailwind v4 theme system, HTMX hooks, page templates, and OpenAI App widgets so you can style consent screens, troubleshooting pages, or CodeCall dashboards inside any MCP server.
All exports live in @frontmcp/ui—bring them into your server, Remix app, or plain Node script without extra bundling.

Why @frontmcp/ui

  • Pure HTML output – Components return strings so you can stream them directly through MCP resources, auth flows, or ngrok tunnels.
  • Platform detection – Helpers detect OpenAI, Claude, Gemini, and local browsers so you can inline scripts when networks are blocked.
  • HTMX everywhere – Buttons, forms, modals, and tables expose htmx options for progressive enhancement.
  • Theme system – Ship the default GitHub/OpenAI skin or mint your own palette, typography, and CDN URLs.
  • Zod validation – Each component validates props at runtime and renders helpful error boxes instead of crashing.

Install

npm install @frontmcp/ui

Compose a page layout

Wrap content with baseLayout, then mix components such as card and button. Everything resolves to HTML strings you can hand to an MCP resource or auth callback.
import { baseLayout, card, button, DEFAULT_THEME } from '@frontmcp/ui';

const content = card(
  `
  <h2 class="text-lg font-semibold">CRM Access</h2>
  <p>Grant the CodeCall orchestrator access to customer data.</p>
  ${button('Approve', { variant: 'primary', type: 'submit' })}
`,
  { variant: 'elevated' },
);

const html = baseLayout(content, {
  title: 'Authorize CRM',
  description: 'Let the agent read CRM data for this session.',
  theme: DEFAULT_THEME,
  width: 'md',
  align: 'center',
  scripts: { tailwind: true, htmx: true },
});
Send html as the body of an MCP resource, OAuth consent page, or even an email.

HTMX-powered interactions

Every interactive component accepts an htmx block. Attributes are sanitized to prevent unsafe protocols or arbitrary data-* fields, so you can expose progressive enhancement safely.
import { form, input, button } from '@frontmcp/ui';

const loginForm = form(
  `
  ${input({ name: 'email', label: 'Email', type: 'email', required: true })}
  ${input({ name: 'code', label: 'One-time code', type: 'text', required: true })}
  ${button('Verify', { type: 'submit', variant: 'primary' })}
`,
  {
    action: '/api/login',
    method: 'post',
    htmx: {
      post: '/api/login',
      target: '#result',
      swap: 'innerHTML',
      indicator: '#loading-indicator',
    },
  },
);

Theme and platform detection

Use createTheme to adjust palettes, typography, and CDN endpoints. Then decide whether to inline scripts (Claude Artifacts) or load from the network (OpenAI, Gemini, ngrok).
import {
  createTheme,
  buildCdnScriptsFromTheme,
  fetchAndCacheScriptsFromTheme,
  getPlatform,
  canUseCdn,
  needsInlineScripts,
} from '@frontmcp/ui';

const theme = createTheme({
  name: 'crm-dark',
  colors: {
    semantic: { primary: '#0BA5EC', secondary: '#6366F1', accent: '#F97316' },
  },
  cdn: {
    scripts: {
      tailwind: 'https://cdn.example.com/tailwind-4.js',
      htmx: { url: 'https://cdn.example.com/htmx.min.js', integrity: 'sha512-abc' },
    },
  },
});

async function buildScripts(forPlatform: string) {
  const platform = getPlatform(forPlatform);

  if (canUseCdn(platform)) {
    return buildCdnScriptsFromTheme(theme);
  }

  if (needsInlineScripts(platform)) {
    await fetchAndCacheScriptsFromTheme(theme);
    return buildCdnScriptsFromTheme(theme, { inline: true });
  }

  return '';
}
Claude Artifacts and other restricted environments block outbound requests. Call fetchAndCacheScriptsFromTheme during build, then inline the cached scripts with buildCdnScriptsFromTheme(..., { inline: true }).

Page templates out of the box

Need a consent or error page fast? Use the prebuilt templates so every auth flow looks consistent.
import { consentPage, consentSuccessPage, errorPage } from '@frontmcp/ui';

const grantScreen = consentPage({
  client: { name: 'CodeCall CRM', id: 'crm' },
  user: { name: 'Ada Lovelace', email: '[email protected]' },
  scopes: [
    { name: 'tools:users:*', description: 'Read and manage CRM users' },
    { name: 'tools:activities:*', description: 'Log activities and view stats' },
  ],
});

const successScreen = consentSuccessPage({
  clientName: 'CodeCall CRM',
  nextSteps: ['Return to the Inspector', 'Share the scoped token with your agent'],
});

const outageScreen = errorPage({
  title: 'CRM temporarily unavailable',
  description: 'We paused the orchestrator while maintenance completes.',
});

Validation and debugging

Components validate input with Zod. When something is wrong, the library renders a compact error box that lists the component name and the failed property—perfect for catching mistakes before you ship.
import { button } from '@frontmcp/ui';

const ok = button('Continue', { variant: 'primary' });

// Invalid variant => returns an error markup instead of throwing
const broken = button('Continue', { variant: 'purple' as any });
Validation errors render in development and production. Keep an eye on your previews to ensure you only ship supported variants, attributes, and HTMX actions.

Widgets and next steps

The widgets module exposes OpenAI App SDK components (resource pickers, action lists) plus status badges, progress indicators, and table helpers. Pair this guide with the CodeCall CRM demo to see the library inside a full orchestration workflow, or expose the HTML through an MCP resource for your own consent flows.