{"revision":"c10276e953456835cdbb1f3a60d58ae1980be353","document":{"id":2781,"path":"application-development/ui-builder/component-builder.md","title":"Create custom components to reuse across pages with component builder","bundle":null,"doc_type":null,"release":"australia","body":"\n# Create custom components to reuse across pages with component builder\n\nReuse custom components across experiences and pages in UI Builder.\n\nCreate custom components in UI Builder's component building UI. The component building UI shares many similarities to page building and enables you to configure components the same way you would when configuring a page in UI Builder.\n\n## The power of custom components\n\nBuilding custom components improves efficiency, consistency, and maintainability across your experience. By creating reusable UI elements, you avoid duplicating configurations, ensure a uniform look and feel, and make it easier to manage updates or design changes. Custom components also simplify complex layouts, support flexible configurations, and enable for easier testing and debugging. They’re also helpful in team environments, where shared components help streamline collaboration and keep the user experience consistent at scale.\n\nCustom components are especially valuable when designing pages for different user types within your experience. For example, you can create a reusable component that includes both a list and a data visualization, then tailor its content or behavior based on the user group enabling you to maintain a consistent layout while delivering role-specific information.\n\nComponents edited in the Component Builder will automatically update on all pages where they are used.\n\n**Important:** Custom components created in an instance remain available only within that instance until they are migrated to other instances using update sets or application installations.\n\n## Custom components or page collections\n\nTo build efficient, scalable digital experiences, it’s important to reuse elements wherever possible. Two ways to do this are through custom components and page collections. Each serves a distinct purpose depending on the scope of reuse that you need.\n\n-   **Custom Components**\n\n    Use custom components when you want to replicate a specific part of a page like a heading, list, form, or buttons across multiple pages.\n\n    You want to apply a consistent theme or configuration to a component or group of components.\n\n    You're designing pages to be modular or flexible where parts of a page may change but shared components remain consistent.\n\n    You want to manage changes in one place and have them updated everywhere the component is used.\n\n    Multiple teams are simultaneously working on different sections of the same page.\n\n-   **Page Collections**\n\n    You want to reuse an entire page layout and configuration across multiple pages or experiences.\n\n    You must build a variation of full pages for different users or workflows.\n\n    You want to use a set of tabs across multiple pages or experiences.\n\n\n## Custom component UI\n\nYou can access the component builder in UI Builder by selecting **Create** in the header or the **Component** tile on the UI Builder home page.\n\n\\[Omitted image \"component-builder-create-buttons.png\"\\] Alt text: UI Builder homepage with arrows pointing the component create buttons.\n\n\\[Omitted image \"component-builder-home.png\"\\] Alt text: Component builder homepage.\n\nComponents built with UI Builder can be found in the toolbox when adding a component to a page and in the component list on the home page of UI Builder. You can update or modify custom components by locating it in the components list on the home page of UI Builder.\n\n\\[Omitted image \"cb-test-values.png\"\\] Alt text: Editing test values for input properties\n\nUse test values in component builder to supply simulated values for required and optional URL parameters when building a custom component. Test values help validate how a component will act when added to a page by ensuring bindings and data resources are functioning correctly. For more information about test values see, [Test values in a page](https://raw.githubusercontent.com/ServiceNow/ServiceNowDocs/australia/markdown/australia/application-development/ui-builder/test-value.md).\n\n\\[Omitted image \"components-list-home.png\"\\] Alt text: UI Builder homepage displaying the components tab.\n\nYou can quickly duplicate a custom component from the component settings screen by selecting **Duplicate**, which creates an exact copy of the component for reuse or modification.\n\n\\[Omitted image \"custom-component-duplicate.png\"\\] Alt text: Component settings screen with an arrow displaying the Duplicate option.\n\n## Component Builder vs NOW CLI Component Development\n\nThere are two ways to build components for UI Builder. The first is using the low-code component within UI Builder, which offers a drag-and-drop interface for creating custom components. The second is using [NOW CLI developer tools](https://developer.servicenow.com/dev.do#!/reference/next-experience/yokohama/cli/getting-started) to build components through code. Both methods produce components that can be added to the UIB toolbox and reused across experiences. Keep in mind that changes to included components may impact both types.\n\nAlthough both tools create components for UI Builder, there are important differences to consider.\n\nComponent Builder in UI Builder:\n\n-   Components built within UI Builder can reference controllers and data resources.\n-   Creates \"Macroponent Components,\" which are stored in the sys\\_ux\\_macroponent table.\n-   Component Builder is ideal for users who prefer a visual, drag-and-drop interface for building components.\n-   Great for quickly creating simple to moderately complex components.\n\nNOW CLI components:\n\n-   Intended for developers who need to write custom HTML, CSS, and JavaScript.\n-   Suitable for building complex and customizable components.\n-   Components created with NOW CLI are stored in the sys\\_uib\\_toolbox\\_component table.\n\n## Best practices\n\nThe UI Builder custom component builder lacks governance capabilities and can lead to duplication and inconsistency in your experience. Teams may create similar components with slight variations, resulting in a fragmented UI, and confusing user experience. It is recommended that your team completes regular audits and cross-team communication to maintain alignment and avoid fragmentation as your experiences grows.\n\nAll components are designed to be upgrade safe, as long as their security policy is set to `read_only`. This provides greater upgrade protection for larger components or page partials compared to other deployable units like bundles and page templates. However, this also means that out-of-the-box \\(OOTB\\) components may not be editable.\n\n**Parent Topic:**[Component Builder](https://raw.githubusercontent.com/ServiceNow/ServiceNowDocs/australia/markdown/australia/application-development/ui-builder/component-builder-uib.md)\n\n## Create components to reuse across pages\n\nBuild reusable custom components to use across experiences and pages in UI Builder.\n\n### Before you begin\n\nRole required: ui\\_builder\\_admin\n\n### About this task\n\nIn this Component Builder example, we will create a stopwatch component to track elapsed time, which can be added to any page. The component will include customizable properties that can be modified once it's placed onto a page.\n\n### Procedure\n\n1.  Navigate to **All** &gt; **Now Experience Framework** &gt; **UI Builder**.\n\n2.  Select **Create** from the UI Builder home page.\n\n    \\[Omitted image \"create-component-button.png\"\\] Alt text: UI Builder home page with the Create component button.\n\n3.  Select **Component**.\n\n4.  In the form, fill in the fields.\n\n    \\[Omitted image \"create-component-form.png\"\\] Alt text: Create a component form.\n\n    |Field|Description|\n    |-----|-----------|\n    |Name|Add a name to track your component internally. The component name appears in the toolbox. For this example, it is `Stopwatch`.|\n    |Categories|Add a category to your component to help easily find it in the toolbox. You can update the category later in the Settings. For this example select **Content**.|\n    |Description|Add description to describe when and how to use your component.|\n    |Icon|Select an icon to represent the component in the toolbox.|\n\n5.  Select **Create**.\n\n    The page opens in Component Builder view.\n\n    \\[Omitted image \"create-component-editor.png\"\\] Alt text: Custom component building UI showing the content and configuration side bars.\n\n6.  Add a custom component property by selecting **+ Add property** in the **Properties and policy** section.\n\n    \\[Omitted image \"cb-add-property.png\"\\] Alt text: Select Add property to add content\n\n    1.  Select **String** from the list.\n\n    2.  Enter the following values in the configuration panel.\n\n        |Field|Value|\n        |-----|-----|\n        |Property label|`SVG Image Source`|\n        |Property ID|`svgImageSource`|\n        |Default value|`animateddino.svg`|\n\n        \\[Omitted image \"cb-component-properties.png\"\\] Alt text: Custom component building UI with arrows pointing to the Property label, Property ID, and SVG Image Source fields in the configuration panel.\n\n    3.  Select **Save**.\n\n7.  Configure the layout for the custom component by selecting the **+ Add Content** button.\n\n    1.  Select **Single column**, then select **Add**.\n\n    2.  In the content tree, under **Column 1**, select **+Add content**.\n\n    3.  Select **Card Base Container**, then select **Add**.\n\n    4.  In the content tree, under **Card Base Container 1**, select **+ Add content**.\n\n    5.  Select **Layouts**.\n\n    6.  Select **Flexbox**, then select **Add**.\n\n    7.  Select **Save**.\n\n        \\[Omitted image \"cb-component-layout.png\"\\] Alt text: Add content to a container\n\n8.  Add a stylized text component in our Flexbox container by selecting **+ Add content** under **Container 1**.\n\n    1.  Select the **Stylized text** component, then select **Add**.\n\n    2.  Select **Cancel** to close the preset window.\n\n    3.  Select **Save**.\n\n        \\[Omitted image \"cb-stylized-text-component.png\"\\] Alt text: Add stylized text to a container\n\n9.  To configure the stylized text component we will add some client state parameters.\n\n    1.  Select **Client state parameters** in the **Data and scripts** section.\n\n    2.  In the **Edit client state parameters** dialog, enter the following values:\n\n        |Name|Type|Initial value|\n        |----|----|-------------|\n        |startTime|Number| |\n        |elapsedTime|String|00:00:00|\n        |timeinterval|JSON| |\n        |stopwatchRunning|Boolean| |\n        |intervalId|String| |\n\n    3.  Select **Apply**.\n\n        \\[Omitted image \"cb-client-state-parameters.png\"\\] Alt text: List of client state parameters added to the custom component.\n\n    4.  Select **Save**.\n\n10. To configure the stylized text component we are going to bind the **elapsedTime** client state parameter.\n\n    1.  Select the **Stylized text 1** component in the content tree.\n\n    2.  Select the bind icon when pointing to the **Text** field of the stylized text component.\n\n        \\[Omitted image \"cb-text-bind-icon.png\"\\] Alt text: Bind stylized text in component\n\n    3.  Select **Client states**.\n\n    4.  Double-click the **elapsedTime** pill.\n\n        \\[Omitted image \"cb-elapsedtime-pill.png\"\\] Alt text: Bind the elapsed time parameter to text\n\n    5.  Select **Apply**.\n\n    6.  Select **Save**.\n\n11. Add a Flexbox container to hold the buttons.\n\n    1.  Select the **+** icon under the stylized text component.\n\n        \\[Omitted image \"cb-add-component-icon.png\"\\] Alt text: Select the plus sign under the text component\n\n    2.  Select **Layouts**.\n\n    3.  Select **Flexbox**.\n\n    4.  Select **Add**.\n\n12. Add and configure the stopwatch running renderer and the start button.\n\n    1.  Select **+ Add content** in the container you added in the previous step and add a **Conditional renderer**.\n\n    2.  Select **+ Add condition** in the content tree.\n\n    3.  Select **Single component**, then select **Next**.\n\n    4.  Select **Button iconic**, then select **Next**.\n\n    5.  In the **Edit settings** dialog, enter the following values:\n\n        -   Component label: `Start`\n        -   Component ID: `start_button`\n        -   Render content: **Always**\n    6.  Select **Apply**.\n\n    7.  Select the **Start** button in the content tree.\n\n    8.  Set the following properties in the configuration panel.\n\n        |Field|Value|\n        |-----|-----|\n        |Icon|**stopwatch-fill**|\n        |Variant|**Primary**|\n        |Size|**Large**|\n        |Tooltip text|`Start`|\n\n    9.  Select **Save**.\n\n        \\[Omitted image \"cb-add-renderer.png\"\\] Alt text: Custom component building UI with arrows pointing to the component label, and Icon, Variant, Size, and Tooltip text fields in the configuration panel.\n\n13. Configure the stop button to display while the stopwatch is running.\n\n    1.  Select **+ Add condition** in the content tree.\n\n    2.  Select **Empty container**, then select **Next**.\n\n    3.  In the **Edit settings** dialog, enter the following values:\n\n        -   Component label: `Running`\n        -   Component ID: `running_container`\n        -   Render content: **When condition below is true**\n    4.  Select the bind icon while pointing at the **Condition** field.\n\n        \\[Omitted image \"cb-condition-bind-icon.png\"\\] Alt text: Edit settings modal where you select to bind\n\n    5.  Select **Client states**, then double-click the **stopwatchRunning** pill.\n\n    6.  Select **Apply**.\n\n    7.  Select **Apply**.\n\n    8.  Select **+ Add content** in the **Running** container and add a **Button iconic**.\n\n    9.  Select **Button iconic 1** in the configuration panel and enter the following values:\n\n        -   Component label: `Stop`\n        -   Component ID: `stop_button`\n    10. Select **Apply**.\n\n    11. Set the following properties in the configuration panel.\n\n        |Field|Value|\n        |-----|-----|\n        |Icon|**stopwatch-fill**|\n        |Variant|**Secondary**|\n        |Size|**Large**|\n        |Tooltip text|`Stop`|\n\n    12. Select **Save**.\n\n        \\[Omitted image \"cb-add-renderer2.png\"\\] Alt text: Custom component building UI with arrows pointing to the component label, and Icon, Variant, Size, and Tooltip text fields in the configuration panel.\n\n14. Reorder the conditions so that **Running** appears above **Start**.\n\n    1.  Select **Conditional renderer 1** in the content tree.\n\n    2.  In the configuration panel, select and drag the drag handle icon \\[Omitted image \"drag-handle.png\"\\] Alt text: to move the conditions into position.\n\n        \\[Omitted image \"cb-reorder-conditions.png\"\\] Alt text: Conditional renderer component in the configuration panel with the \"Running\" condition placed near the \"Start\" condition.\n\n        Conditions are evaluated in order from top to bottom, so **Running** must appear above **Start** for the buttons to display correctly.\n\n15. Add the logic for the component by selecting **+** next to **Client scripts** in the **Data and scripts** section.\n\n    1.  Enter `Start` in the **Script name** field.\n\n    2.  Paste the following script into the script field.\n\n        ```\n        function handler({ api, helpers }) {\n          console.log('Start script running');\n         \n          if (api.state.stopwatchRunning === undefined) {\n            api.setState('stopwatchRunning', false);\n            api.setState('elapsedTime', '00:00:00');\n            api.setState('startTime', null);\n            api.setState('intervalId', null);\n          }\n         \n          let running = api.state.stopwatchRunning;\n         \n          function pad(n) {\n            return n < 10 ? '0' + n : n.toString();\n          }\n         \n          function updateElapsedTime(startTime) {\n            if (!running) {\n              if (api.state.intervalId) {\n                helpers.timing.clearInterval(api.state.intervalId);\n                api.setState('intervalId', null);\n                console.log('Interval cleared');\n              }\n              return;\n            }\n         \n            if (!startTime) {\n              console.log('No startTime passed to updateElapsedTime, cannot update');\n              return;\n            }\n         \n            const now = Date.now();\n            const elapsedMs = now - startTime;\n         \n            const totalSeconds = Math.floor(elapsedMs / 1000);\n            const hours = pad(Math.floor(totalSeconds / 3600));\n            const minutes = pad(Math.floor((totalSeconds % 3600) / 60));\n            const seconds = pad(totalSeconds % 60);\n         \n            const formattedTime = `${hours}:${minutes}:${seconds}`;\n         \n            console.log('Setting elapsedTime:', formattedTime);\n            api.setState('elapsedTime', formattedTime);\n          }\n         \n          if (!running) {\n            const now = Date.now();\n         \n            api.setState('startTime', now);\n            api.setState('elapsedTime', '00:00:00');\n            api.setState('stopwatchRunning', true);\n            running = true;\n         \n            if (api.state.intervalId) {\n              helpers.timing.clearInterval(api.state.intervalId);\n              api.setState('intervalId', null);\n              console.log('Existing interval cleared before starting new one');\n            }\n         \n            // Pass startTime directly here\n            updateElapsedTime(now);\n         \n            const id = helpers.timing.setInterval(() => {\n              updateElapsedTime(now);\n            }, 1000);\n            api.setState('intervalId', id);\n            console.log('Interval started with id:', id);\n          } else {\n            if (!api.state.intervalId) {\n              // Use existing startTime from state, but be mindful it might still lag\n              const storedStartTime = api.state.startTime || Date.now();\n              const id = helpers.timing.setInterval(() => {\n                updateElapsedTime(storedStartTime);\n              }, 1000);\n              api.setState('intervalId', id);\n              console.log('Interval restarted with id:', id);\n            }\n          }\n        }\n        ```\n\n    3.  Select **Apply**.\n\n        \\[Omitted image \"cb-add-client-script.png\"\\] Alt text: Edit client script modal overlaying the UI Builder editor.\n\n    4.  Select **+** next to **Client scripts** to add a second script.\n\n    5.  Enter `Stop` in the **Script name** field.\n\n    6.  Paste the following script into the script field.\n\n        ```\n        function handler({ api, helpers }) {\n          console.log('Stop script running');\n         \n          if (api.state.stopwatchRunning === undefined) {\n            // If state is not initialized yet, initialize it to avoid errors\n            api.setState('stopwatchRunning', false);\n            api.setState('elapsedTime', '00:00:00');\n            api.setState('startTime', 0);\n            api.setState('intervalId', null);\n          }\n         \n          if (api.state.stopwatchRunning) {\n            api.setState('stopwatchRunning', false);\n            if (api.state.intervalId) {\n              helpers.timing.clearInterval(api.state.intervalId);\n              api.setState('intervalId', null);\n            }\n          }\n        }\n        ```\n\n    7.  Select **Apply**.\n\n    8.  Select **Save**.\n\n16. Add event handlers to the buttons we created in the previous steps.\n\n    1.  Select the **Start** button in the content tree and open the **Events** tab in the configuration panel.\n\n    2.  Select **Add handler** under **Button iconic clicked**, then select the **Start** client script we created in the previous steps.\n\n        \\[Omitted image \"cb-add-event.png\"\\] Alt text: Add event handler modal with an arrow pointing to a client script labeled \"Start.\"\n\n    3.  Select **Continue**, then select **Add**.\n\n    4.  Select the **Stop** button in the content tree and open the **Events** tab in the configuration panel.\n\n    5.  Select **Add handler**, then select the **Stop** client script we created in the previous steps.\n\n    6.  Select **Continue**, then select **Add**.\n\n    7.  Select **Save**.\n\n17. Select **Preview** to test the configured components.\n\n    \\[Omitted image \"cb-preview.png\"\\] Alt text: UI Builder preview of page with running stopwatch component.\n\n\n### Result\n\nYour custom component is now available in the UI Builder toolbox.\n\n"}}