Portal integration
How to wire @sumx/sumx-editor into the SumX Client Portal (or any Next.js 14+ Pages Router app).
Dependencies
package.json (feed semver example):
{
"dependencies": {
"@sumx/sumx-editor": "0.1.0",
"@sumx/ui": "0.1.2",
"suneditor": "^3.1.1",
"react-easy-crop": "^5.5.0"
},
"devDependencies": {
"@sumx/ui": "0.1.2"
}
}Use pnpm run sumx:local / sumx:vendor / sumx:feed per Consume in your app.
Next.js config
Add the editor package to transpilePackages so Next compiles kit output correctly:
// next.config.js
const nextConfig = {
transpilePackages: [
'@sumx/ssr-auth-core',
'@sumx/http-client',
'@sumx/sumx-editor',
],
};Styles
Global SCSS — editor chrome, variable chips, list classes:
/* src/styles/globals.scss */
@import '@sumx/sumx-editor/styles.scss';Tailwind — scan kit class names (border-gray-gray, text-black-soft, etc.) with the portal theme:
/* src/styles/tailwind.css */
@source "../../node_modules/@sumx/ui/dist";
@source "../../node_modules/@sumx/ui/src";
@source "../../node_modules/@sumx/sumx-editor/dist";
@source "../../node_modules/@sumx/sumx-editor/src";Theme tokens live in the host tailwind.config.js (gray.gray, black.soft, primary, …). The editor uses those names, not generic zinc-* scales.
Permission provider
SumXEditor uses @sumx/ui (Button, Dialog, Input, DropdownMenu). Wrap the app with your portal SumxUiPermissionProvider (or any PermissionProvider) the same as for auth dialogs — see Permissions.
Dynamic import for route-level code splitting
For heavy screens, lazy-load the editor shell (the package already avoids SSR for SunEditor internally):
import dynamic from 'next/dynamic';
const SumXEditor = dynamic(() => import('@sumx/sumx-editor'), {
ssr: false,
loading: () => (
<div className="min-h-[120px] animate-pulse rounded-md border border-gray-gray bg-gray-lighter/40" aria-hidden />
),
});Do not statically import suneditor or sumx-editor-core from files that Next executes on the server.
Feature module pattern
| Layer | Responsibility |
|---|---|
schemas/ | Zod — store string HTML from the editor |
services/ | API — send/receive HTML as provided by backend |
components/ | SumXEditor + onChange bound to form state |
| Detail / list previews | RichTextContent from @sumx/sumx-editor/rich-text-content |
React Hook Form example:
import { Controller, useFormContext } from 'react-hook-form';
import SumXEditor from '@sumx/sumx-editor';
function BodyField() {
const { control } = useFormContext<{ body: string }>();
return (
<Controller
name="body"
control={control}
render={({ field }) => (
<SumXEditor
value={field.value ?? ''}
onChange={field.onChange}
toolbarPreset="standard"
invalid={/* from formState.errors.body */}
/>
)}
/>
);
}Sanitizer re-export (optional)
Portal keeps a thin shim so existing imports keep working:
// src/shared/lib/sanitize-rich-html.ts
export {
RICH_HTML_SANITIZE_CONFIG,
sanitizeRichHtml,
} from '@sumx/sumx-editor/sanitize-rich-html';Prefer @sumx/sumx-editor/sanitize-rich-html in new code.
Portal feature examples
Typical imports in the client portal:
| Feature | Editor | Read view |
|---|---|---|
| Notifications / announcements | @sumx/sumx-editor | @sumx/sumx-editor/rich-text-content |
| Email composer | @sumx/sumx-editor | — |
| Manual invoice / standard text | dynamic(() => import('@sumx/sumx-editor')) | rich-text-content |
| Bill processing email log | — | rich-text-content |
@sumx/ui version
@sumx/sumx-editor@0.1.0 requires @sumx/ui ≥ 0.1.2 (Input, Label, Slider, DropdownMenu moved from editor into UI). Keep both on the same feed/local/vendor mode.