--- title: "Bring Your Own (BYO) 7.26 Implementation Examples" slug: "bring-your-own-byo-726-implementation-examples" updated: 2025-12-03T18:58:34Z published: 2025-12-09T16:46:27Z canonical: "docs.unqork.io/bring-your-own-byo-726-implementation-examples" --- > ## Documentation Index > Fetch the complete documentation index at: https://docs.unqork.io/llms.txt > Use this file to discover all available pages before exploring further. # Bring Your Own (BYO) 7.26 Implementation Examples > [!WARNING] > The [Bring Your Own](/v1/docs/introduction-to-the-bring-your-own-byo-framework) feature is intended for developers who have a strong understanding of the Unqork Designer Platform and JavaScript. ## Overview The **Bring Your Own (BYO) 7.26 implementation examples** page contains example implementations of a component, event, and operation. Refer to these exampless when building your components with the [BYO Component SDK](/v1/docs/bring-your-own-byo-component-sdk-software-development-kit) guide. AG Grid ComponentTask List ComponentRoot Level ComponentAvg & Round Operation ## AG Grid Component This example displays the customization options, `manifest.json`, and implementation of a custom AG Grid component. ### Configuration | Settings | Object Type | Description | | --- | --- | --- | | `columnDefinitions` | string componentKey | The definitions of the columns from the `key` and `label` data. | | `value` | string componentKey | The value of the grid data. This version accepts a string, but it can be defined as needed. | ### Events | Event Name | Event Value | Description | | --- | --- | --- | | Cell Clicked | `agGridCellClicked` | Add a custom event that emits on cell click. **Event Payload**: Add the details of the cell that was clicked into the Event Payload to use in logic: - Row of the cell that was clicked. - Column of the cell that was clicked. - Value of the cell that was click. | ## Example AG Grid Package Click on each of the tabs below to view the JSON and Javascript required to implement an AG Grid: ### Manifest JSON Example > Get an in-depth understanding of `manifest.json` specifications in our [Bring Your Own (BYO): Understanding the manifest.json File](/v1/docs/bring-your-own-byo-understanding-the-manifestjson-file) article. ```json {  "name": "AgGrid",  "version": "1.0.0",  "main": "agGrid.js",  "type": "custom",  "productType": "BYO",  "description": "AG Grid integration with Unqork",  "components": [    {      "name": "AG Grid",      "type": "agGrid",      "description": "AG Grid component for Unqork",      "model": {        "type": "object",        "properties": {          "columnDefinitions": {            "name": "Column Definitions",            "type": "array",            "description": "Column definitions for this grid (currently only supporting key and label)",            "items": {              "type": "object",              "properties": {                "key": {                  "type": "string",                  "description": "Unique key for the column"                },                "label": {                  "type": "string",                  "description": "Display label for the column"                }              }            }          },          "value": {            "name": "Grid Data (JSON)",            "type": "string",            "description": "JSON string of data for the grid"          }        },        "required": ["columnDefinitions"]      },      "events": [        {          "name": "AG Grid Cell Clicked",          "type": "agGridCellClicked",          "description": "This event is triggered when the user clicks on a grid cell.",          "stability": "STABLE"        }      ]    }  ] } ``` ### agGrid.js The implementation javascript file name must match the `manifest.json`'s `main` value. In this example, the `agGrid.js` file matches line four in the manifest. ```javascript class UnqorkAgGridComponent extends HTMLElement {  constructor() {    super()    this.attachShadow({ mode: 'open' })    this.gridOptions = {      columnDefs: [],      rowData: [],    }  }  /**   * This is the only required method to integrate with the Unqork system   *   * @param {*} api The interface to the Unqork Runtime, used for both   *                monitoring state and emitting events   */  initialize(api) {    this.config = api.state.currentState()    this.api = api    if (this.config.columnDefinitions?.length) {      const defs = this._normalizeInput(this.config.columnDefinitions)      this.gridOptions.columnDefs = this._formatColumnDefinitions(defs)    }    if (this.config.value?.length) {      this.gridOptions.rowData = this._normalizeInput(this.config.value)    }    this.renderGrid(this.shadowRoot, this.gridOptions)    this.subscribeColumnDefs()    this.subscribeData()  }  /**   * Subscribe to the source for column definitions   *   * This can be made more robust by first using   * api.state.state$('columnDefsReference') to subscribe to   * any changes to that value (i.e. the target key), and then using   * resolveByKey$ to resolve the current value of that key, but   * keeping it simple for example here   */  subscribeColumnDefs() {    this      .api      .state      .state$('columnDefinitions').subscribe((columnDefs) => {        console.log('AG Grid column definitions updated to', columnDefs)        // This _should_ be an array, but sometimes components try to set it as a        // JSON string instead, so we handle that here        const value = typeof(columnDefs) === 'string' ? JSON.parse(columnDefs) : columnDefs        this.gridOptions = {          rowData: this.gridOptions.rowData,          columnDefs: value ? this._formatColumnDefinitions(value) : [],        }        this.updateGrid(this.gridOptions)      })  }  subscribeData() {    this.api.state.state$('value').subscribe((rowData) => {      console.log('AG Grid value updated to', rowData)      // We support setting this as a JSON string or already parsed array of objects      // so handle that here      const value = typeof(rowData) === 'string' ? JSON.parse(rowData) : rowData      this.gridOptions = {        rowData: value || [],        columnDefs: this.gridOptions.columnDefs,      }      this.updateGrid(this.gridOptions)    })  }  updateGrid(gridOptions) {    console.log('Updating AG Grid', gridOptions)    this.gridApi.updateGridOptions(gridOptions)  }  async renderGrid(document, gridOptions) {    console.log('Rendering AG Grid component', {gridOptions, agGrid, windowAgGrid: window.agGrid})    document.innerHTML = this.view()    this.gridEl = document.querySelector('#myGrid')    this.gridApi = window.agGrid.createGrid(this.gridEl, gridOptions)    // Set this here so that it doesn't overridden when we update gridOptions    this.gridApi.addEventListener('cellClicked', (ev) => {      console.log('Emitting AG Grid Cell Clicked Event', ev)      this.emitCellClickedEvent(ev)    })    console.log('Created AG Grid', {      document,      gridOptions,      gridEl: this.gridEl,      gridApi: this.gridApi,    })  }  // Emit an event when a cell is clicked  // Full docs: https://www.ag-grid.com/javascript-data-grid/grid-events/#reference-selection-cellClicked  emitCellClickedEvent = (ev) => {    this.api.events.emit({      name: 'onAgGridCellClicked',      payload: ev    })  }  view() {    return `      
   `  }  /**   * Input data can be either JSON string or actual data, due to   * Unqork data constraints, so ensure it's proper data before   * using   */  _normalizeInput(val) {    return typeof(val) === 'string' ? JSON.parse(val) : val  }  /**   * We store column definitions with standard key/label properties   * But AG Grid using different property names, so we map it here   */  _formatColumnDefinitions(defs) {    return defs.map(({key, label}) => {      return {        field: key,        headerName: label ?? key      }    })  } } // This definition defines the state settings (model) for this component // It mirrors the fuller description in manifest.json and will // eventually be used to auto-generate the manifest.json JSON schema class AgGridDefinition {  columnDefsReference  dataReference } // Actual export that exposes both the view layer and the model // and is consumed by the Unqork Runtime for both configuration and // execution // All of these are `async` to allow for us to load them on demand // without impacting initial page load performance export const agGrid = {  model: async () => AgGridDefinition,  view: async () => UnqorkAgGridComponent, } // This definition describes the schema of the event that is emitted // when the user clicks on an item class CellClicked {  name = 'agGridCellClicked'  payload = {    index,    text,  } } // This export is used to expose the event to the Unqork Runtime export const agGridCellClicked = {  model: async () => CellClicked, }     ``` > Understand what's required to build a custom component in [BYO Component SDK](/v1/docs/bring-your-own-byo-component-sdk-software-development-kit) article. ## Task List Component This example displays the customization options, `manifest.json`, and implementation of a custom Task List component. This asset also contains an event and an operation. > Discover how to create a custom task list component in our [How To Example: Bring Your Own Task List](/v1/docs/bring-your-own-byo-component-sdk-software-development-kit) article. ### Configuration | Settings | Type | Object Type | Description | | --- | --- | --- | --- | | `label` | Input | string componentKey | The title that displays above the task list. Default value is "Tasks". | | `value` | Input | array componentKey | An array of objects that defines the task items in the list. Each object in the array contains the following: - `text` (string): The text of the task item. - `completed` (boolean): Whether the task item's checkbox is checked. Default value is `false` (unchecked). | ### Event | Event Name | Event Value | Description | | --- | --- | --- | | Task List Item Toggled | `taskListItemToggled` | An event that emits when a user checks or unchecks a task item. The event includes a payload with the text and completed status of the updated item. **Event Payload**: Adds the item name of the item that was clicked. | ### Operation | Event Name | Event Value | Description | | --- | --- | --- | | Task List Toggle Task List Item | `toggleTaskListItem` | Toggles or explicitly sets the completed state of a single item in a target task list list. Includes the following settings: - `targetKey` (string, required): component key of the task list to modify. - `item` (number\|string, required) : either the index of the item or the exact text to match. - `completed` (boolean, optional): A boolean to set the `completed` property of item. If not provided, toggles the existing value to the opposite. For example, `true` (checked) becomes `false` (unchecked), or, `false` (unchecked) becomes `true` (checked). | ## Example Task List Package Click on each of the tabs below to view the JSON and Javascript required to implement a Task List component, including an event and operation: ### Manifest JSON Example > Get an in-depth understanding of `manifest.json` specifications in our [Bring Your Own (BYO): Understanding the manifest.json File](/v1/docs/bring-your-own-byo-understanding-the-manifestjson-file) article. ```json {  "name": "TaskList",  "version": "1.0.0",  "main": "taskList.js",  "type": "custom",  "productType": "BYO",  "description": "A simple task list component with an event and an operation.",  "components": [    {      "name": "Example Task List",      "type": "taskList",      "description": "Simple task list with checkboxes.",      "model": {        "type": "object",        "properties": {          "label": {            "name": "Label",            "type": "string",            "description": "Label of Task List",            "default": "Tasks"          },          "value": {            "name": "Value",            "type": "array",            "description": "Array of task items",            "items": {              "type": "object",              "properties": {                "text": {                  "type": "string",                  "description": "Text to display for the task item"                },                "completed": {                  "type": "boolean",                  "description": "Whether the item is completed",                  "default": false                }              },              "required": ["text"]            },            "default": []          }        },        "required": ["value"]      },      "events": [        {          "name": "Example Task List Item Toggled",          "type": "taskListItemToggled",          "description": "Emitted when an end-user checks/unchecks a task item.",          "stability": "STABLE",          "model": {            "type": "object",            "properties": {              "name": {                "const": "taskListItemToggled",                "type": "string",                "description": "Name of the event"              },              "payload": {                "type": "object",                "description": "The event payload.",                "properties": {                  "item": {                    "type": "object",                    "description": "A task list item.",                    "properties": {                      "text": { "type": "string", "description": "Task name."},                      "completed": { "type": "boolean", "description": "Whether this task is completed."}                    }                  }                },                "required": [                  "item"                ]              }            }          }        }      ],      "operations": [        {          "name": "Example Task List Toggle Task List Item",          "type": "toggleTaskListItem",          "description": "Toggle (or set) the completion state of a specific item on a target task list.",          "stability": "STABLE",          "model": {            "type": "object",            "properties": {              "options": {                "type": "object",                "properties": {                  "targetKey": {                    "name": "Target Key",                    "type": "string",                    "description": "Component key of the task list to modify"                  },                  "item": {                    "name": "Item",                    "description": "Index (number) or text (string) of the item to change",                    "type": ["number", "string"]                  },                  "completed": {                    "name": "Completed",                    "type": "boolean",                    "description": "Optional explicit completed value; if omitted, the value is toggled"                  }                }              },              "type": {                "const": "toggleTaskListItem",                "type": "string"              }            },            "required": ["targetKey", "item"]          }        }      ]    }  ] } ``` ### taskList.js The implementation JavaScript file name must match the `manifest.json`'s `main` value. In this example, the `taskList.js` file matches line four in the manifest. ```javascript class UnqorkTaskListComponent extends HTMLElement {  constructor() {    super()    this.attachShadow({ mode: 'open' })    this.items = []    this.label = 'Tasks'  }  /**   * This is the only required method to integrate with the Unqork system   *   * @param {*} api The interface to the Unqork Runtime, used for both   *                monitoring state and emitting events   */  initialize(api) {    this.api = api    this.config = api.state.currentState()    // Initial label and value from config (if provided)    if (this.config.label != null) this.label = String(this.config.label)    if (this.config.value != null) this.items = this._normalizeItems(this.config.value)    this.render(this.shadowRoot, this.label, this.items)    // Subscribe to state changes such as SET_PROPERTY    this.subscribeLabel()    this.subscribeValue()  }  /**   * Subscribes to changes in the `label` property of the component's state.   *   * This method listens to the `state$('label')` observable for updates to the `label` property.   * When a new value is emitted, it updates the `label` property of the component and re-renders the component.   *   * @returns {void}   */  subscribeLabel() {    this.api.state.state$('label').subscribe((label) => {      this.label = label ?? ''      this.shadowRoot.innerHTML = this.view(this.label, this.items)    })  }  /**   * Subscribes to changes in the `value` property of the component's state.   *   * This method listens to the `state$('value')` observable for updates to the `value` property.   * When a new value is emitted, it normalizes the value into an array of items and updates the `items` property.   * It then re-renders the component with the updated items.   *   * @returns {void}   */  subscribeValue() {    this.api.state.state$('value').subscribe((value) => {      this.items = this._normalizeItems(value)      this.shadowRoot.innerHTML = this.view(this.label, this.items)    })  }  /**   * Normalizes the input value into an array of objects with a specific structure.   *   * This method ensures that the input `value` is converted into an array of objects,   * where each object contains the following properties:   * - text: The text to actually display in the taskList item (defaults to an empty string if missing).   * - completed: A boolean indicating whether the item is completed (defaults to `false` if missing).   *   * @param value - The input value to normalize. Can be a JSON string or an array.   * @returns The normalized array of items.   */  _normalizeItems(value) {    const arr = typeof value === 'string' ? JSON.parse(value) : value    if (!Array.isArray(arr)) return []    return arr.map((item) => ({      text: String(item?.text ?? ''),      completed: Boolean(item?.completed),    }))  }  render(root, label, items) {    root.innerHTML = this.view(label, items)    root.addEventListener('change', (ev) => {      const el = ev.target      if (!(el instanceof HTMLInputElement) || el.type !== 'checkbox') return      const idx = Number(el.getAttribute('data-index'))      if (Number.isNaN(idx) || !this.items[idx]) return      const updatedItem = { ...this.items[idx], completed: el.checked }      const next = [...this.items]      next[idx] = updatedItem      // Update state with new item value      this.api.state.set({ value: next })      // Emit taskListItemToggled event      this.emitItemToggledEvent(updatedItem)    })  }  /**   * Emits a `taskListItemToggled` event when a to-do list item is toggled.   *   * This method triggers a domain event with the name `taskListItemToggled` and includes   * the toggled item's details in the event payload. The payload contains the updated item   * with its `text` and `completed` properties.   *   */  emitItemToggledEvent = (item) => {    this.api.events.emit({      name: 'taskListItemToggled',      payload: { item },    })  }  // The component view  view(label, items) {    const list = items      .map(        (item, i) => `        
  •                  
  • `,      )      .join('')    return `    
    ${label ?? ''}
         `  } } /** * This definition defines the state settings (model) for this component * It mirrors the fuller description in manifest.json and will * eventually be used to auto-generate the manifest.json schema */ class TaskListDefinition {  label  value } /** * This export provides the view and model definitions for the component, * which are utilized by the Unqork Runtime for configuration and execution. * Both definitions are `async` to enable on-demand loading, improving initial page load performance. * * Component export name MUST match manifest `type` */ export const taskList = {  model: async () => TaskListDefinition,  view: async () => UnqorkTaskListComponent, } /** ========= Example Task List Item Toggled Event Model ========= **/ // Defines the structure of the payload for the taskListItemToggled event class TaskListItemToggledPayload {  item = { text: '', completed: false } } // This definition describes the schema of the event that is emitted // when the user toggles an item class TaskListItemToggled {  // The name of the event  name = 'taskListItemToggled'  // The details of the toggled to-do list item  payload = new TaskListItemToggledPayload() } // This export is used to expose the event to the Unqork Runtime // Note: event export name MUST match manifest `type` export const taskListItemToggled = {  model: async () => TaskListItemToggled, } /** ======== Example Task List Toggle Task List Item Operation ======== **/ // Operation options/schema for toggling a task list item class ToggleTaskListItemOptions {  // key of the taskList component to modify  targetKey  // number (index) OR string (text) of the item to change  item  // Boolean to set completed property of item to. If not provided, just toggle the existing value (e.g. true -> false and false -> true)  completed } class ToggleTaskListItemOperation {  // MUST match the manifest "type" and the named export below  type = 'toggleTaskListItem'  options = new ToggleTaskListItemOptions() } class ToggleTaskListItemHandler {  /**   * Execute the operation.   * @param {ToggleTaskListItemOperation} operation   * @param {*} api Operation API: { state: { resolveByKey, set, get }, events: { emit } }   */  async execute(operation, api) {    const { targetKey, item, completed } = operation.options.config || {}    if (!targetKey) throw new Error('toggleTaskListItem Operation: "targetKey" is required.')    if (item === undefined || item === null)      throw new Error('toggleTaskListItem Operation: "item" (index or text) is required.')    // Resolve the target component state    const target = api.state.resolveByKey(targetKey)    // Expecting a list of items like the following:    // [    //   { "text": "Buy groceries", "completed": false },    //   { "text": "Complete design doc", "completed": true }    // ]    const list = target?.value ?? []    if (!Array.isArray(list)) throw new Error('toggleTaskListItem Operation: target value is not an array.')    // Find the target index    let index = typeof item === 'number' ? item : list.findIndex((it) => String(it?.text ?? '') === String(item))    // Ensure the index is within bounds, otherwise throw an error    if (index < 0 || index >= list.length) {      throw new Error(`toggleTaskListItem Operation: could not find item "${item}".`)    }    // Get the current item or default to an empty object with default properties    const current = list[index] ?? { text: '', completed: false }    // Determine the next completion state based on the provided `completed` boolean or toggle the current state    const nextCompleted = typeof completed === 'boolean' ? completed : !current.completed    // Create a new list with the updated item at the specified index    const newList = [...list]    newList[index] = { ...current, completed: nextCompleted }    // Update the target component state with the new list    api.state.set(targetKey, { value: newList })  } } // The following class is omitted since the operation handler does not return a result // class ToggleTaskListItemContext { //   ... // } // Named export — MUST match manifest "type" export const toggleTaskListItem = {  model: async () => ToggleTaskListItemOperation,  handler: async () => ToggleTaskListItemHandler,  //This is omitted since the operation handler does not return a result  //contextModel: async () => ToggleTaskListItemContext, } ``` > Understand what's required to build a custom component in [BYO Component SDK](/v1/docs/bring-your-own-byo-component-sdk-software-development-kit) article. ## Root Level Event This example displays the Manifest JSON and JavaScript implementation of a custom event. After uploading a package containing an event asset, Creators can use the event in the [Operations Builder](/v1/docs/operations-builder-eventsreference). Click on each of the tabs below to view the JSON and Javascript required to implement a root level event: ### Manifest JSON Example > Get an in-depth understanding of `manifest.json` specifications in our [Bring Your Own (BYO): Understanding the manifest.json File](/v1/docs/bring-your-own-byo-understanding-the-manifestjson-file) article. ```json {  "name": "eventsOnly",  "version": "1.0.0",  "main": "eventsOnly.js",  "type": "custom",  "productType": "BYO",  "description": "Asset that contains only general events.",  "events": [    {      "name": "Events Only",      "type": "eventsOnly",      "description": "A top-level event.",      "stability": "ALPHA",      "model": {        "type": "object",        "properties": {          "name": {            "const": "eventsOnly",            "type": "string"          },          "payload": {            "type": "object",            "properties": {              "data": {                "description": "Sample property",                "type": "string"              }            }          }        }      }    }  ] } ``` ### eventsOnly.js The implementation JavaScript file name must match the `manifest.json`'s `main` value. In this example, the `eventsOnly.js` file matches line four in the manifest. ```javascript class EventPayload {  data = 'defaultValue' } class Event {  name = 'eventsOnly'  payload = new EventPayload() } // Export for the event export const eventsOnly = {  model: async () => Event, } ``` > Understand what's required to build a custom component in [BYO Component SDK](/v1/docs/bring-your-own-byo-component-sdk-software-development-kit) article. ## Average and Round Operation This example displays an example of the Manifest JSON and JavaScript implementation of a custom averaging and rounding operation. After uploading a package containing an operation asset, Creators can use the operation in the [Operations Builder](https://docs.unqork.io/Content/A05-ModEditor-Data_Event/A05018-Operations_Builder.htm). ### Configuration Options | Settings | Type | Object Type | Description | | --- | --- | --- | --- | | `numbers(num[], optional` | Input | Array componentKey | The array of numbers to average. By default this value is `[ ]` (none). | | `roundNumDec ( integer, optional)` | Input | Integer | An integer representing the number of decimal places for rounding. - **Constrained**: 0 –10 - **Default**: 2 - **Placeholder**: “Specify an integer between 0 and 10” | ### Payload | Settings | Description | | --- | --- | | `result` (`number`, `required`) | The averaged value after rounding to the requested decimals. | ## Average and Round Operation Package Click on each of the tabs below to view the JSON and Javascript required to implement a root level operation: ### Manifest JSON Example > Get an in-depth understanding of `manifest.json` specifications in our [Bring Your Own (BYO): Understanding the manifest.json File](/v1/docs/bring-your-own-byo-understanding-the-manifestjson-file) article. ```json {  "name": "exAvgAndRound",  "version": "1.0.0",  "main": "exAvgAndRoundOperation.js",  "type": "custom",  "productType": "BYO",  "description": "A simple operation that averages an array of numbers and rounds result to specified decimals.",  "operations": [    {      "name": "Example Average and Round",      "type": "exAvgAndRound",      "description": "Averages an array of numbers and rounds result to specified decimals.",      "stability": "STABLE",      "model": {        "type": "object",        "properties": {          "options": {            "type": "object",            "properties": {              "numbers": {                "name": "Numbers",                "type": "array",                "description": "Flat array of numbers to average.",                "items": { "type": "number" },                "default": []              },              "roundNumDec": {                "name": "Round Number Decimals",                "type": "integer",                "description": "Number of decimals to round result.",                "minimum": 0,                "maximum": 10,                "default": 2,                "placeholder": "specify an integer between 0 and 10"              }            }          },          "type": {            "const": "exAvgAndRound",            "type": "string"          }        }      },      "contextModel": {        "type": "object",        "properties": {          "result": {            "type": "object",            "properties": {              "result": {                "name": "Result",                "type": "number",                "description": "Result of the average operation."              }            }          }        }      }    }  ] } ``` ### exAvgAndRoundOptions.js The implementation JavaScript file name must match the `manifest.json`'s `main` value. In this example, the `exAvgAndRoundOptions.js` file matches line four in the manifest. ```javascript // Operation options class ExAvgAndRoundOptions {  // Flat array of numbers to average. Defaults to an empty array  numbers = []  // Integer decimals (0..10) to round the result. Defaults to 2  roundNumDec = 2 } // Operation model class ExAvgAndRoundOperation {  // MUST match the manifest "type" and the named export below  type = 'exAvgAndRound'  options = new ExAvgAndRoundOptions() } // Operation handler class ExAvgAndRoundHandler {  /**   * Execute the operation.   * @param {ExAvgAndRoundOperation} operation   * @param {*} api Operation API: { state: { resolveByKey, set, get }, events: { emit } }   * @returns {{ result: number }}   */  async execute(operation, api) {    // Get the operation options or default to an empty object    const opts = operation.options.config ?? {}    // Extract the numbers array from the options    const nums = opts.numbers    // Calculate the mean (average).    // The result is either 0 (if the array is empty) or the calculated mean of the numbers.    const mean = nums.length === 0 ? 0 : nums.reduce((acc, n) => acc + n, 0) / nums.length    // Round the mean to the specified number of decimals    // Javascript method toFixed returns a string so it needs to be coerced back to number    const decimals = opts.roundNumDec    const result = Number(mean.toFixed(decimals))    return { result }  } } // Context result (what the op returns into context) // If the operation handler does not return a result. This can be omitted. class ExAvgAndRoundContextResult {  result = 0 } // If the operation handler does not return a result. This can be omitted. class ExAvgAndRoundContext {  result = new ExAvgAndRoundContextResult() } // Export for the operation // Named export MUST match manifest "type" export const exAvgAndRound = {  model: async () => ExAvgAndRoundOperation,  handler: async () => ExAvgAndRoundHandler,  contextModel: async () => ExAvgAndRoundContext, // If the operation handler does not return a result. This can be omitted. } ``` > Understand what's required to build a custom component in [BYO Component SDK](/v1/docs/bring-your-own-byo-component-sdk-software-development-kit) article. ## Resources ### [Bring Your Own (BYO) Framework Landing Page](/v1/docs/bring-your-own-byo-framework-landingpage) Find all the resources needed to build your own components here. ### [Introduction to the Bring Your Own (BYO) Framework](/v1/docs/introduction-to-the-bring-your-own-byo-framework) Get started building your own custom assets, including components, events, and operations. ### [Create a BYO Package](/v1/docs/bring-your-own-byo-create-a-package) Discover the structure and content of a BYO Package ### [Understanding the manifest.json File](/v1/docs/bring-your-own-byo-understanding-the-manifestjson-file) Learn about the file's specification, component and event definitions. ### [Understanding the BYO Runtime Engine API](/v1/docs/bring-your-own-byo-understanding-runtime-engine-api) Discover the interface between your custom BYO components and the Unqork platform. ### [Custom Component SDK](/v1/docs/bring-your-own-byo-component-sdk-software-development-kit) Extend component functionality in the Unqork Designer Platform. ### [Custom Component](/v1/docs/bring-your-own-byo-custom-component) Understand the state, interface, and events of a custom component. ### [Custom Events](/v1/docs/bring-your-own-byo-custom-events) Learn more about creating custom events for use in the Operations Builder. ### [Event Payloads](/v1/docs/bring-your-own-byo-726-event-payloads) Learn more about viewing and using event payloads with the Operations Builder. ### [Custom Operations](/v1/docs/bring-your-own-byo-custom-operations) Learn more about creating custom operations for use in the Operations Builder. ### [BYO Best Practices](/v1/docs/best-practices-bring-your-own-byo-framework) Learn about best practices for implementing and using BYO assets. ### [BYO General FAQ](/v1/docs/bring-your-own-byo-general-faq-frequently-asked-questions) Learn about the Bring Your Own Framework and discover frequently asked questions. JavaScript is an object-oriented computer programming language. It is most-commonly used for interactive effects in the browser. A string is an object that represents a sequence of characters. Strings typically hold data represented in text form. JSON (JavaScript Object Notation) is an open standard file and data interchange format. Unqork uses JSON for submission (record) data. Also known as Unqork Users, or Designer Users; is anyone who is inside the Unqork platform.