Build Code Components
Code components extend Momen’s frontend with custom React and TypeScript. Build interfaces and interactions that are not available through built-in components, publish them as a component library, and import the library into a Momen project.
Common uses include data visualizations, maps, advanced forms, animations, and third-party frontend libraries. The zvm-code-context package connects a component to the host project’s data, user identity, GraphQL client, and runtime component tree.
Start a project
Before you begin, you should be comfortable with React, TypeScript, npm, and the Momen editor. You also need a working Node.js development environment.
Install the CLI
npm install --global momen-cliList the commands available in your installed version:
momen --helpSign in to Momen
momen signin <username-or-email> <password>The CLI stores the local session used to create and publish code component projects.
Create a project
Run these commands from your working directory:
momen create my-components
cd my-components
npm installcreate generates the local template and registers the corresponding code component project under the current Momen account.
Understand the project structure
Most development work takes place in src/components, src/graphql, resources, and package.json:
- index.ts
- Main.tsx
- style.module.scss
- index.ts
- package.json
- README.md
- vite.config.ts
src/componentscontains component implementations and the exports included in the published library.src/graphqlcontains GraphQL operations used by your components.resourcescontains library and component icons, covers, and canvas previews.package.jsondefines the library name, version, description, category, and supported platforms.README.mdexplains the purpose and use of the library.
Build a component
Keep each component in its own directory. Both the directory and component names must begin with an uppercase letter. For a component named Counter:
src/components/Counter/
├── Counter.tsx
├── index.ts
└── style.module.scssDuring publishing, the CLI reads three interfaces and converts their fields into configurable Momen properties, states, and events:
| Interface | Purpose | Declaration |
|---|---|---|
${ComponentName}PropData | Input data bound from Momen. | Use a supported primitive or array type; fields may be optional. |
${ComponentName}StateData | State the component can read, update, and expose to Momen. | Use State<T>. |
${ComponentName}Event | Frontend events the component can trigger. | Use EventHandler. |
The Props interface types the React component itself. It is not a separate configuration interface parsed by the CLI.
Complete example
import { EventHandler, State } from 'zvm-code-context';
export interface CounterPropData {
label: string;
step?: number;
}
export interface CounterStateData {
value?: State<number>;
}
export interface CounterEvent {
onChange?: EventHandler;
}
export interface CounterProps {
propData: CounterPropData;
propState: CounterStateData;
event: CounterEvent;
}
export function Counter({ propData, propState, event }: CounterProps) {
const value = propState.value?.get() ?? 0;
const increase = async () => {
await propState.value?.set(value + (propData.step ?? 1));
event.onChange?.();
};
return (
<button type="button" onClick={increase}>
{propData.label}: {value}
</button>
);
}Export the component from its directory:
export * from './Counter';Then register every component to publish in src/components/index.ts:
import { Counter } from './Counter';
export default {
Counter,
};The publisher recognizes string, number, boolean, and arrays of these types. State fields use the corresponding State<T>, and event fields use EventHandler. The template also exports specialized date, time, and JSON types. The current CLI analysis result is authoritative for publishable types.
Use host APIs
Call useAppContext() inside a component to access the current Momen page, user identity, GraphQL client, and runtime component tree.
import { useAppContext } from 'zvm-code-context';
const ctx = useAppContext();navigate
Navigate to a path in the host application.
ctx.navigate('/orders/10001');The current signature is navigate(path). Build path and query parameters into the URL before calling it; a second configuration argument is not supported.
query
Send a GraphQL query or mutation to the project backend with the current host identity.
const response = await ctx.query(
`query GetPosts($limit: Int!) {
post(limit: $limit) {
id
title
}
}`,
{ limit: 10 }
);
const posts = response.data?.post;query returns the standard GraphQL JSON response. Handle both data and errors.
subscribe
Start a GraphQL subscription with the current host identity and handle each data update.
const subscription = ctx.subscribe(
`subscription ListenPost($id: bigint!) {
post_by_pk(id: $id) {
id
status
}
}`,
{ id: 100000000000001 },
(data) => {
console.log(data.post_by_pk);
}
);
subscription.unsubscribe();The callback receives the GraphQL data object. Call unsubscribe() when the component unmounts or no longer needs updates.
globalData
Read the current app-level variable object.
const locale = ctx.globalData?.locale;globalData is the current value rather than a State object. To update app-level variables through runtime state, use discover('App').
pageData
Read or update the current page variable object.
const pageData = ctx.pageData.get();
await ctx.pageData.set({
...pageData,
selectedOrderId: 10001,
});pageData is a State object. Use get() and set() instead of mutating its properties directly.
apolloClient
Access the Apollo Client instance used by the host application. Use it when you need Apollo-specific features such as fetch policies, cache access, or manual refetching.
import GetPosts from '@/graphql/getPosts.gql';
const result = await ctx.apolloClient.query({
query: GetPosts,
variables: { limit: 10 },
fetchPolicy: 'network-only',
});Prefer query and subscribe for ordinary GraphQL operations. Use apolloClient only when the additional Apollo behavior is necessary.
getLoggedInUserAuth
Get the current signed-in user’s authentication data asynchronously.
const auth = await ctx.getLoggedInUserAuth();
console.log(auth.userId);
console.log(auth.userRoles);The result contains userId, userToken, and userRoles. It may also include thirdPartyInfo for a third-party sign-in.
userToken is the current user’s credential. Do not log it, send it to an untrusted service, or persist it in public storage.
component
Access the runtime component instance associated with the current code component. It exposes the current instance’s state, variables, runtime engine, and component-tree capabilities.
const currentComponent = ctx.component;
const engine = currentComponent.engine;Runtime component objects contain platform state. Before changing a value, use the field’s public get(), set(), or other supported methods instead of replacing object properties directly.
discover
Find a component instance in the host application by its runtime component ID.
const app = ctx.discover('App');
const appState = (app.state || app.variables).App_CustomState;
const current = appState.get();
await appState.set({
...current,
locale: 'en-US',
});discover returns a runtime component instance, so its available fields depend on the target component type. Verify that the component ID and state exist before using them.
Configure the library
Review these package.json fields before publishing:
| Field | Description |
|---|---|
name | Component library name. |
description | Library description. |
version | Version to publish. It must follow npm versioning and must not duplicate an existing release. |
homepage | Optional demo or introduction URL. |
category | Component category, such as container, display, input, or others. |
platforms | Supported runtime platforms. Momen component libraries use WEB. |
The resource directory supports the following files:
resources/icon.webporresources/icon.svgfor the library icon.resources/{ComponentName}/icon.webpor.svgfor a component icon.resources/{ComponentName}/cover.webpor.svgfor a component cover.resources/{ComponentName}/canvas.webpor.svgfor the canvas preview.- Root
README.mdfor library documentation. src/components/{ComponentName}/README.mdfor component documentation.
Debug components
Preview the build locally
npm run build
npm run previewThe preview server uses port 6326 by default.
Connect to Momen real-time preview
The component library must have at least one published version before a Momen project can replace it with a local debug build.
- Run
npm run preview. - In another terminal, run
npm run watch:buildto rebuild after source changes. - Open the debug entry in Momen real-time preview and select port
6326. - Refresh real-time preview after changing the code.
Publish and import
Check the project
momen dryrunThis checks the project structure, component declarations, and build process before publishing.
Publish the library
momen publishUpdate version in package.json before each release. To display detailed execution output, run:
momen publish --verboseUse the library in Momen
Choose Import Component Library from the component panel and import the published library. You can then drag its components to the canvas and configure their properties, states, and events.
After publishing a new version, update the library version in every Momen project that uses it.
CLI commands
| Command | Purpose |
|---|---|
momen signin / signout | Sign in to or out of Momen. |
momen create <project-name> | Create and register a code component project. |
momen dryrun | Check whether the current project can be published. |
momen publish | Build and publish a new version. |
momen list <project-name> | List published versions. |
momen update | Migrate an older project to the current template structure. Commit or stash local changes first. |
momen reinit | Register the current local project as a new project. |
momen unpublish <version> | Withdraw a published version. |
momen delete [project-name] | Delete a project; without a name, delete the project associated with the current directory. |
reset, reinit, unpublish, and delete change the local project or remote release state. Confirm the target project and preserve your local code before running them.
Troubleshooting
| Problem | Resolution |
|---|---|
| The CLI reports that you are not signed in | Run momen signin again. |
| The CLI cannot find a component | Check the component directory, file names, three configuration interfaces, and the export in src/components/index.ts. |
| Properties, states, or events are missing | Confirm that every field uses a supported type, then run momen dryrun --verbose to inspect parser errors. |
| The version already exists | Update version in package.json and publish again. |
| Local changes do not appear in real-time preview | Confirm that both watch:build and preview are running, then refresh real-time preview. |
| A GraphQL request fails | Inspect both data and errors in the response. See the Runtime API Reference for generated API structures. |