Auto-Generated Packages
Overview​
Myop's Auto-Generated Packages feature automatically creates framework-specific npm packages for your Myop components. These packages provide fully typed, tree-shakeable wrappers that integrate seamlessly with your application's framework, enabling a superior developer experience with IDE autocompletion and type safety.
Table of Contents​
- How It Works
- Benefits
- Installation
- Framework-Specific Guides
- Type Definitions
- Configuration Options
- Best Practices
How It Works​
When you create a Myop HTML component with type definitions, the Myop server automatically generates npm-installable packages for each supported framework. These packages are:
- Generated on-demand: Packages are created dynamically when requested via the npm install URL
- Framework-specific: Each package includes proper peer dependencies and idioms for its target framework
- Fully typed: TypeScript declarations are extracted from your component's
<script type="myop/types">block - Thin wrappers: The actual component logic runs at runtime - packages only contain typed wrapper code (~6KB total)
Package Generation Flow​
Your HTML Component
↓
<script type="myop/types">
interface MyopInitData { ... }
interface MyopCtaPayloads { ... }
</script>
↓
Myop Server extracts types
↓
Generates framework-specific package
↓
Returns npm-installable .tgz
Benefits​
| Benefit | Description |
|---|---|
| Type Safety | Full TypeScript support with IDE autocompletion for data and events |
| Zero Bundle Bloat | ~6KB total for all components regardless of quantity |
| Hot Updates | Update components without redeploying your application |
| Developer Experience | Typed event handlers auto-generated from your CTA definitions |
| Framework Native | Packages feel native to each framework's patterns and idioms |
| No Build Step | Install directly from URL - no compilation required |
Installation​
Auto-generated packages are installed directly from the Myop API using a URL-based npm install:
# General pattern
npm install https://cloud.myop.dev/npm/{component-id}/{framework}
# Example for React
npm install https://cloud.myop.dev/npm/abc123-def456/react
# Example for Vue
npm install https://cloud.myop.dev/npm/abc123-def456/vue
# Example for Angular
npm install https://cloud.myop.dev/npm/abc123-def456/angular
# Example for TypeScript/Vanilla JS
npm install https://cloud.myop.dev/npm/abc123-def456/typescript
# Example for React Native
npm install https://cloud.myop.dev/npm/abc123-def456/react-native
Package.json Reference​
You can also add packages directly to your package.json:
{
"dependencies": {
"@myop/users-table": "https://cloud.myop.dev/npm/abc123-def456/react",
"@myop/dashboard-widget": "https://cloud.myop.dev/npm/xyz789-uvw012/react"
}
}
Framework-Specific Guides​
React​
Installation​
# Install the host package (required)
npm install @myop/react @myop/sdk
# Install your auto-generated component
npm install https://cloud.myop.dev/npm/{component-id}/react
Usage​
import { UsersTable } from "@myop/users-table";
function App() {
const users = [
{ id: 1, name: "Alice", email: "alice@example.com", role: "Admin" },
{ id: 2, name: "Bob", email: "bob@example.com", role: "User" },
];
return (
<UsersTable
data={{ rows: users }}
onRowClicked={(payload) => {
console.log("Selected user:", payload.rowData);
}}
onDeleteClicked={(payload) => {
deleteUser(payload.userId);
}}
onExportRequested={(payload) => {
exportToFormat(payload.format);
}}
style={{ width: 600, height: 400 }}
/>
);
}
Generated Package Structure​
The React package generates a forwardRef wrapper component:
import { MyopComponent } from "@myop/react";
import { forwardRef } from "react";
export const UsersTable = forwardRef<any, UsersTableProps>((props, ref) => (
<MyopComponent
{...props}
componentId="{component-id}"
ref={ref}
/>
));
Peer Dependencies​
react >= 17.0.0@myop/react
Vue​
Installation​
# Install the host package (required)
npm install @myop/vue @myop/sdk
# Install your auto-generated component
npm install https://cloud.myop.dev/npm/{component-id}/vue
Usage​
<script setup lang="ts">
import { UsersTable } from "@myop/users-table";
import { ref } from "vue";
const users = ref([
{ id: 1, name: "Alice", email: "alice@example.com", role: "Admin" },
{ id: 2, name: "Bob", email: "bob@example.com", role: "User" },
]);
function handleRowClicked(payload: { rowIndex: number; rowData: any }) {
console.log("Selected user:", payload.rowData);
}
function handleDeleteClicked(payload: { userId: string }) {
deleteUser(payload.userId);
}
</script>
<template>
<UsersTable
:data="{ rows: users }"
@row-clicked="handleRowClicked"
@delete-clicked="handleDeleteClicked"
:style="{ width: '600px', height: '400px' }"
/>
</template>
Generated Package Structure​
The Vue package generates a defineComponent wrapper:
import { defineComponent, h } from "vue";
import { MyopComponent } from "@myop/vue";
export const UsersTable = defineComponent({
name: "UsersTable",
props: { /* typed props */ },
emits: ["load", "error", "sizeChange", "cta", "row-clicked", "delete-clicked"],
setup(props, { emit }) {
return () => h(MyopComponent, {
...props,
componentId: "{component-id}",
onLoad: (component) => emit("load", component),
onCta: (action, payload) => emit(action, payload),
});
},
});
Peer Dependencies​
vue >= 3.0.0@myop/vue
Angular​
Installation​
# Install the host package (required)
npm install @myop/angular @myop/sdk
# Install your auto-generated component
npm install https://cloud.myop.dev/npm/{component-id}/angular
tsconfig.json Configuration​
For Angular, you must configure TypeScript to compile from node_modules:
{
"compilerOptions": {
"outDir": "./out-tsc/app"
},
"include": [
"src/**/*.d.ts",
"src/**/*.ts",
"node_modules/@myop/**/*.ts"
]
}
Usage​
import { Component } from "@angular/core";
import { UsersTable } from "@myop/users-table";
@Component({
selector: "app-root",
standalone: true,
imports: [UsersTable],
template: `
<users-table
[data]="{ rows: users }"
(rowClicked)="onRowClicked($event)"
(deleteClicked)="onDeleteClicked($event)"
[style]="{ width: '600px', height: '400px' }"
/>
`,
})
export class AppComponent {
users = [
{ id: 1, name: "Alice", email: "alice@example.com", role: "Admin" },
{ id: 2, name: "Bob", email: "bob@example.com", role: "User" },
];
onRowClicked(payload: { rowIndex: number; rowData: any }) {
console.log("Selected user:", payload.rowData);
}
onDeleteClicked(payload: { userId: string }) {
this.deleteUser(payload.userId);
}
}
Generated Package Structure​
The Angular package generates a standalone component:
import { Component, Input, Output, EventEmitter } from "@angular/core";
import { MyopComponent } from "@myop/angular";
@Component({
selector: "users-table",
standalone: true,
imports: [MyopComponent],
template: `
<myop-component
[componentId]="componentId"
[data]="data"
(load)="load.emit($event)"
(cta)="handleCta($event)"
/>
`,
})
export class UsersTable {
componentId = "{component-id}";
@Input() data: UsersTableData | undefined;
@Output() load = new EventEmitter<any>();
@Output() rowClicked = new EventEmitter<RowClickedPayload>();
@Output() deleteClicked = new EventEmitter<DeleteClickedPayload>();
handleCta(event: { action: string; payload: any }) {
// Route to typed output
}
}
Peer Dependencies​
@angular/core >= 17.0.0@myop/angular
TypeScript (Vanilla)​
For vanilla JavaScript/TypeScript applications without a framework.
Installation​
# Install the SDK (required)
npm install @myop/sdk
# Install your auto-generated component
npm install https://cloud.myop.dev/npm/{component-id}/typescript
Usage​
import { createUsersTable } from "@myop/users-table";
async function initComponent() {
const usersTable = await createUsersTable({
container: document.getElementById("users-table-container"),
data: {
rows: [
{ id: 1, name: "Alice", email: "alice@example.com", role: "Admin" },
{ id: 2, name: "Bob", email: "bob@example.com", role: "User" },
],
},
onLoad: (component) => {
console.log("Component loaded:", component);
},
onError: (error) => {
console.error("Error:", error);
},
// Generic CTA handler
onCta: (action, payload) => {
console.log("Action:", action, "Payload:", payload);
},
// Typed action handlers (auto-generated from CTA definitions)
onRowClicked: (payload) => {
console.log("Row clicked:", payload.rowData);
},
onDeleteClicked: (payload) => {
deleteUser(payload.userId);
},
});
// Wait for initialization
await usersTable.initiated();
// Update data
usersTable.update({ rows: newUsers });
// Get current data
const currentData = usersTable.getData();
// Subscribe to specific actions
usersTable.on("export-requested", (payload) => {
exportToFormat(payload.format);
});
// Cleanup
usersTable.dispose();
}
Generated Package Structure​
The TypeScript package generates a factory function:
import { hostSDK } from "@myop/sdk/host";
import { CloudRepository } from "@myop/sdk/helpers";
export interface CreateUsersTableOptions {
container: HTMLElement;
data?: UsersTableData;
onLoad?: (component: any) => void;
onError?: (error: string) => void;
onCta?: (action: string, payload: any) => void;
onRowClicked?: (payload: RowClickedPayload) => void;
onDeleteClicked?: (payload: DeleteClickedPayload) => void;
}
export async function createUsersTable(options: CreateUsersTableOptions) {
const config = await CloudRepository.Main.fetchComponentV2("{component-id}");
const component = await hostSDK.loadComponent(config, options.container, {
data: options.data,
});
// Setup handlers and return interface...
return {
update: (data) => component.props.myop_init_interface(data),
getData: () => component.props,
on: (action, handler) => { /* subscribe */ },
onRowClicked: (handler) => { /* typed subscribe */ },
dispose: () => component.dispose(),
initiated: () => component.initiated(),
};
}
Peer Dependencies​
@myop/sdk
React Native​
For React Native mobile applications using WebView.
Installation​
# Install the host package (required)
npm install @myop/react-native react-native-webview
# Install your auto-generated component
npm install https://cloud.myop.dev/npm/{component-id}/react-native
Usage​
import React, { useState } from "react";
import { View, StyleSheet } from "react-native";
import { UsersTable } from "@myop/users-table";
function App() {
const [component, setComponent] = useState(null);
const users = [
{ id: 1, name: "Alice", email: "alice@example.com", role: "Admin" },
{ id: 2, name: "Bob", email: "bob@example.com", role: "User" },
];
return (
<View style={styles.container}>
<UsersTable
data={{ rows: users }}
onLoad={(proxy) => setComponent(proxy)}
onError={(error) => console.error("Error:", error)}
onRowClicked={(payload) => {
console.log("Row clicked:", payload.rowData);
}}
onDeleteClicked={(payload) => {
deleteUser(payload.userId);
}}
style={styles.table}
scrollEnabled={false}
zoomEnabled={false}
/>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1 },
table: { flex: 1 },
});
Component Proxy Interface​
The onLoad callback provides a proxy for programmatic control:
interface IMyopComponentProxy {
id: string;
props: {
myop_init_interface: (data: any) => void;
myop_cta_handler: ((action: string, payload?: any) => void) | null;
};
element: {
set(path: string, value: any): void;
get(path: string): Promise<any>;
style: {
set(property: string, value: string): void;
get(property: string): Promise<string>;
};
};
dispose(): void;
hide(): void;
show(): void;
inspect(): void;
}
Generated Package Structure​
The React Native package generates a forwardRef wrapper with useImperativeHandle:
import { MyopComponent } from "@myop/react-native";
import { forwardRef, useImperativeHandle } from "react";
export const UsersTable = forwardRef<any, UsersTableProps>((props, ref) => {
const componentRef = useRef(null);
useImperativeHandle(ref, () => ({
update: (data) => componentRef.current?.props.myop_init_interface(data),
getComponent: () => componentRef.current,
}));
return (
<MyopComponent
{...props}
componentId="{component-id}"
onLoad={(proxy) => {
componentRef.current = proxy;
props.onLoad?.(proxy);
}}
/>
);
});
Peer Dependencies​
react >= 17.0.0react-native >= 0.70.0@myop/react-native
Type Definitions​
For auto-generated packages to extract types, your Myop HTML component must include a <script type="myop/types"> block:
<!DOCTYPE html>
<html>
<head>
<script type="myop/types">
// Data passed to the component via myop_init_interface
interface MyopInitData {
rows: Array<{
id: number;
name: string;
email: string;
role: string;
}>;
columns?: string[];
sortable?: boolean;
}
// Events emitted via myop_cta_handler
interface MyopCtaPayloads {
"row-clicked": {
rowIndex: number;
rowData: any;
};
"delete-clicked": {
userId: string;
};
"export-requested": {
format: "csv" | "json" | "xlsx";
};
}
</script>
</head>
<body>
<!-- Component HTML -->
</body>
</html>
Type Naming Conventions​
The package generator looks for these type names (in order of preference):
MyopInitData- Data interface for component initialization*Props,*Input,*Config- Alternative naming patterns- First
interfaceortypedeclaration
For CTA payloads:
MyopCtaPayloads- CTA event payloads- Second
interfaceortypedeclaration
Action Name Conversion​
CTA action names in kebab-case are automatically converted to PascalCase for typed handlers:
| CTA Action | React Handler | Vue Event | Angular Output |
|---|---|---|---|
row-clicked | onRowClicked | @row-clicked | (rowClicked) |
delete-clicked | onDeleteClicked | @delete-clicked | (deleteClicked) |
export-requested | onExportRequested | @export-requested | (exportRequested) |
Configuration Options​
Preloading Components​
Preload components for faster rendering:
// React
import { preloadComponents, isPreloaded } from "@myop/react";
await preloadComponents(["component-1", "component-2"]);
if (isPreloaded("component-1")) {
console.log("Component will render instantly");
}
// Vue
import { preloadComponents, isPreloaded } from "@myop/vue";
await preloadComponents(["component-1", "component-2"]);
// Angular
import { preloadComponents, isPreloaded } from "@myop/angular";
await preloadComponents(["component-1", "component-2"]);
Custom Cloud Repository​
Configure a custom Myop server URL:
// React
import { setCloudRepositoryUrl, enableLocalDev } from "@myop/react";
// Production custom endpoint
setCloudRepositoryUrl("https://custom.myop.dev");
// Local development (localhost:9292)
enableLocalDev();
Environment and Preview Modes​
Load components from different environments:
// React - Load from staging
<UsersTable
data={data}
environment="staging"
/>
// React - Load unpublished preview
<UsersTable
data={data}
preview={true}
/>
// React - Staging preview
<UsersTable
data={data}
environment="staging"
preview={true}
/>
<!-- Vue -->
<UsersTable
:data="data"
environment="staging"
:preview="true"
/>
<!-- Angular -->
<users-table
[data]="data"
environment="staging"
[preview]="true"
/>
Best Practices​
1. Define Clear Type Contracts​
Always provide comprehensive type definitions in your component:
interface MyopInitData {
// Document each field
/** List of items to display */
items: Item[];
/** Optional title for the component */
title?: string;
/** Theme configuration */
theme?: "light" | "dark";
}
interface MyopCtaPayloads {
/** Fired when an item is selected */
"item-selected": {
itemId: string;
itemData: Item;
};
}
2. Use Semantic Action Names​
Use descriptive, kebab-case action names:
// Good
"file-uploaded"
"user-logged-in"
"form-submitted"
"row-selected"
// Avoid
"click"
"action1"
"doThing"
3. Leverage Preloading​
Preload components on app initialization for instant rendering:
// In your app's entry point
import { preloadComponents } from "@myop/react";
// Preload critical components
preloadComponents([
"dashboard-widget",
"navigation-menu",
"user-profile",
]);
4. Handle Loading and Error States​
Always provide loading and error handling:
// React
import { MyopLoader, MyopFallback } from "@myop/react";
<UsersTable
data={data}
loader={<MyopLoader />}
fallback={<MyopFallback />}
fadeDuration={300}
/>
<!-- Vue - Using slots -->
<UsersTable :data="data" :fadeDuration="300">
<template #loader>
<CustomLoader />
</template>
<template #fallback>
<ErrorMessage @retry="reload" />
</template>
</UsersTable>
5. TypeScript Project Configuration​
For Angular projects, ensure your tsconfig.json includes node_modules:
{
"include": [
"src/**/*.ts",
"node_modules/@myop/**/*.ts"
]
}