React

UI Components

Drop-in React components for teacher dashboards, file upload, and payment flows.

All components are pure Tailwind — no shadcn/ui dependency. Wrap your app in <NexaProvider> once and every component picks up the correct API base path automatically.

Setup

app/layout.tsx
import { NexaProvider } from "@nexa-ed/react";
 
export default function RootLayout({ children }) {
  return (
    <NexaProvider basePath="/api/nexa"> {/* basePath is optional, defaults to /api/nexa */}
      {children}
    </NexaProvider>
  );
}

ResultsViewerSheet

A slide-over panel with three tabs: Pipeline, Records, and Stats.

import { ResultsViewerSheet } from "@nexa-ed/react";
 
<ResultsViewerSheet
  fileId="file_xyz"       // optional — shows document selector if omitted
  userId="user_abc"       // optional — resolved server-side if omitted
/>

UploadZone

Drag-and-drop file upload wired to nexa.files.submit().

import { UploadZone } from "@nexa-ed/react";
 
<UploadZone
  userId="user_abc"
  onFileSubmitted={(fileId) => console.log("Submitted:", fileId)}
  accept=".pdf"
/>

DocumentSelector

Lists a user's uploaded documents with processing status badges.

import { DocumentSelector } from "@nexa-ed/react";
 
<DocumentSelector
  userId="user_abc"
  onSelect={(fileId) => setSelectedFile(fileId)}
  selectedFileId={selectedFile}
/>

StatusBanner

Shows the current processing status for a file (queued → extracting → refining → complete).

import { StatusBanner } from "@nexa-ed/react";
 
<StatusBanner fileId="file_xyz" userId="user_abc" />

PipelineTimeline

Detailed step-by-step OCR pipeline visualization with per-stage timing.

import { PipelineTimeline } from "@nexa-ed/react";
 
<PipelineTimeline fileId="file_xyz" userId="user_abc" />

StudentRecordsTable

Drop-in, fully self-contained records table. Pass only a fileId — fetching, pagination, filters, inline editing, column sort/reorder, bulk operations, export (XLSX / CSV / JSON), import sync with diff preview, and roster matching are all built in.

import { StudentRecordsTable } from "@nexa-ed/react";
 
<StudentRecordsTable fileId="file_xyz" />

See OCR Records UI for the full feature list, all props, and a custom-page example.


RecordDataGrid

Lower-level records grid. All features opt-in via props — omit a prop group to disable it entirely.

import { RecordDataGrid } from "@nexa-ed/react";
import type { RecordRow } from "@nexa-ed/react";
 
<RecordDataGrid
  records={records}
  onSave={handleSave}
  onAddRows={handleAddRows}
  onDeleteRow={handleDelete}
  sortCol={sortCol}
  sortDir={sortDir}
  onToggleSort={toggleSort}
  rosterEntries={rosterMap}
  onAssignRoster={handleAssign}
/>

See OCR Records UI for the complete props reference.


PageDetailPanel

Per-page record review with the original scanned image alongside extracted fields.

import { PageDetailPanel } from "@nexa-ed/react";
 
<PageDetailPanel
  fileId="file_xyz"
  userId="user_abc"
  pageNumber={3}
/>

StatsPanel

Subject pass/fail rates, average scores, and grade distribution charts.

import { StatsPanel } from "@nexa-ed/react";
 
<StatsPanel fileId="file_xyz" userId="user_abc" />

NexaPaymentWidget

Standalone payment widget: loads fee config, displays the total, opens Paystack checkout, and polls for completion.

import { NexaPaymentWidget } from "@nexa-ed/react";
 
<NexaPaymentWidget
  email="[email protected]"
  netAmount={5000_00}          // kobo — school receives this amount
  metadata={{ userId, category: "enrollment" }}
  onSuccess={(ref) => router.push(`/success?ref=${ref}`)}
  onError={(err) => toast.error(err.message)}
/>

Props

PropTypeDescription
emailstringCustomer email for Paystack
netAmountnumberAmount school receives (kobo). Widget auto-calculates gross.
metadataPaymentMetadataPassed through to webhook
onSuccess(reference: string) => voidCalled when payment is confirmed
onError(error: PaymentError) => voidCalled on failure
referencestring?Custom reference; auto-generated if omitted

EnrollmentPaymentFlow

3-step flow: fee breakdown → payment → confirmation receipt.

import { EnrollmentPaymentFlow } from "@nexa-ed/react";
 
<EnrollmentPaymentFlow
  email="[email protected]"
  studentName="Jane Doe"
  examFee={2000_00}             // kobo
  enrollmentFee={3000_00}       // kobo — optional
  metadata={{ userId, category: "enrollment" }}
  onComplete={(ref) => markEnrolled(ref)}
/>

PaymentConfigPanel

Admin UI to view and update the platform fee configuration.

import { PaymentConfigPanel } from "@nexa-ed/react";
 
<PaymentConfigPanel />

Renders a form with fee percentage, fixed fee, and bank account fields. Calls GET/POST /api/nexa/payments/config automatically.


PaymentStatusDashboard

Transaction list with search, status filter, date range, and CSV export.

import { PaymentStatusDashboard } from "@nexa-ed/react";
 
<PaymentStatusDashboard defaultPageSize={20} />

StudentEmailAccountManager

Create, list, suspend, restore, and reset passwords for student email accounts. All actions go through your /api/student-emails/* routes — no API key is exposed to the browser.

import { StudentEmailAccountManager } from "@nexa-ed/react";
 
<StudentEmailAccountManager
  tier="tier-2-stalwart"
  domain="loretto.edu.ng"
  onAccountCreated={(account) => console.log("Created:", account.email)}
/>

Props

PropTypeRequiredDescription
tierEmailTierYesEmail tier: "tier-1-nexa" | "tier-2-stalwart" | "tier-3-google"
domainstringYesEmail domain, e.g. "loretto.edu.ng"
basePathstringNoAPI proxy base path. Defaults to ""
onAccountCreated(account: StudentEmail) => voidNoCalled after successful account creation

Warning

Live key required. Accounts created through this component call your live /api/student-emails/create endpoint. Use a test key during development — you will receive a sandboxed response with _testMode: true.


EmailBulkProvisioningPanel

Provision up to 500 student accounts at once with live job progress tracking. Fires a bulk job and polls for completion every 3 seconds automatically.

import { EmailBulkProvisioningPanel } from "@nexa-ed/react";
 
// Option A — paste JSON manually in the textarea
<EmailBulkProvisioningPanel
  tier="tier-2-stalwart"
  domain="loretto.edu.ng"
  onJobComplete={(progress) =>
    console.log(`Done: ${progress.successCount}/${progress.totalStudents}`)
  }
/>
 
// Option B — pre-load a students array (skips textarea)
<EmailBulkProvisioningPanel
  tier="tier-2-stalwart"
  domain="loretto.edu.ng"
  students={[
    { studentId: "stu_001", firstName: "Amara", lastName: "Obi" },
    { studentId: "stu_002", firstName: "Chidi", lastName: "Eze" },
  ]}
/>

Props

PropTypeRequiredDescription
tierEmailTierYesEmail tier
domainstringYesEmail domain
studentsEmailCreateFormData[]NoPre-load students array — skips textarea
basePathstringNoAPI proxy base path. Defaults to ""
onJobComplete(progress: EmailJobProgress) => voidNoCalled when job reaches completed or failed

On this page