Skip to main content

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​

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:

  1. Generated on-demand: Packages are created dynamically when requested via the npm install URL
  2. Framework-specific: Each package includes proper peer dependencies and idioms for its target framework
  3. Fully typed: TypeScript declarations are extracted from your component's <script type="myop/types"> block
  4. 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​

BenefitDescription
Type SafetyFull TypeScript support with IDE autocompletion for data and events
Zero Bundle Bloat~6KB total for all components regardless of quantity
Hot UpdatesUpdate components without redeploying your application
Developer ExperienceTyped event handlers auto-generated from your CTA definitions
Framework NativePackages feel native to each framework's patterns and idioms
No Build StepInstall 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.0
  • react-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):

  1. MyopInitData - Data interface for component initialization
  2. *Props, *Input, *Config - Alternative naming patterns
  3. First interface or type declaration

For CTA payloads:

  1. MyopCtaPayloads - CTA event payloads
  2. Second interface or type declaration

Action Name Conversion​

CTA action names in kebab-case are automatically converted to PascalCase for typed handlers:

CTA ActionReact HandlerVue EventAngular Output
row-clickedonRowClicked@row-clicked(rowClicked)
delete-clickedonDeleteClicked@delete-clicked(deleteClicked)
export-requestedonExportRequested@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"
]
}

Additional Resources​