

# Using a custom widget on a CloudWatch dashboard
<a name="add_custom_widget_dashboard"></a>

A *custom widget* is a CloudWatch dashboard widget that can call any AWS Lambda function with custom parameters. It then displays the returned HTML or JSON. Custom widgets are a simple way to build a custom data view on a dashboard. If you can write Lambda code and create HTML, you can create a useful custom widget. Additionally, Amazon provides several prebuilt custom widgets that you can create without any code.

When you create a Lambda function to use as a custom widget, we strongly recommend that you include the prefix **customWidget** in the function name. This helps you know which of your Lambda functions are safe to use when you add custom widgets to your dashboard.

Custom widgets behave like other widgets on your dashboard. They can be refreshed and auto-refreshed, resized, and moved around. They react to the time range of the dashboard.

If you have set up CloudWatch console cross-account functionality, you can add a custom widget created in one account to dashboards in other accounts. For more information, see [Cross-account cross-Region CloudWatch console](Cross-Account-Cross-Region.md).

You can also use custom widgets on your own website by using the CloudWatch dashboard sharing feature. For more information, see [Sharing CloudWatch dashboards](cloudwatch-dashboard-sharing.md).

**Topics**
+ [Details about custom widgets in CloudWatch](add_custom_widget_dashboard_about.md)
+ [Security and JavaScript for custom CloudWatch widgets](add_custom_widget_dashboard_security.md)
+ [Interactivity in the custom widget in CloudWatch](add_custom_widget_dashboard_interactivity.md)
+ [Creating a custom widget for a CloudWatch dashboard](add_custom_widget_dashboard_create.md)
+ [Sample custom widgets for a CloudWatch dashboard](add_custom_widget_samples.md)

# Details about custom widgets in CloudWatch
<a name="add_custom_widget_dashboard_about"></a>

Custom widgets work as follows:

1. The CloudWatch dashboard calls the Lambda function containing the widget code. It passes in any custom parameters that are defined in the widget.

1. The Lambda function returns a string of HTML, JSON, or Markdown. Markdown is returned as JSON in the following format:

   ```
   {"markdown":"markdown content"}
   ```

1. The dashboard displays the returned HTML or JSON.

If the function returns HTML, most HTML tags are supported. You can use Cascading Style Sheets (CSS) styles and Scalable Vector Graphics (SVG) to build sophisticated views.

The default style of HTML elements such as links and tables follow the styling of CloudWatch dashboards. You can customize this style by using inline styles, using the `<style>` tag. You can also deactivate the default styles by including a single HTML element with the class of `cwdb-no-default-styles`. The following example deactivates default styles: `<div class="cwdb-no-default-styles"></div>`.

Every call by a custom widget to Lambda includes a `widgetContext` element with the following contents, to provide the Lambda function developer with useful context information.

```
{
  "widgetContext": {
    "dashboardName": "Name-of-current-dashboard",
    "widgetId": "widget-16",
    "accountId": "012345678901",
    "locale": "en",
    "timezone": {
      "label": "UTC",
      "offsetISO": "+00:00",
      "offsetInMinutes": 0
    },
    "period": 300,
    "isAutoPeriod": true,
    "timeRange": {
      "mode": "relative",
      "start": 1627236199729,
      "end": 1627322599729,
      "relativeStart": 86400012,
      "zoom": {
        "start": 1627276030434,
        "end": 1627282956521
      }
    },
    "theme": "light",
    "linkCharts": true,
    "title": "Tweets for Amazon website problem",
    "forms": {
      "all": {}
    },
    "params": {
      "original": "param-to-widget"
    },
    "width": 588,
    "height": 369
  }
}
```

## Default CSS styling
<a name="add_custom_widget_styling"></a>

Custom widgets provide the following default CSS styling elements:
+ You can use the CSS class **btn** to add a button. It turns an anchor (`<a>`) into a button as in the following example:

  ```
  <a class="btn" href=https://amazon.com”>Open Amazon</a>
  ```
+ You can use the CSS class **btn btn-primary** to add a primary button.
+ The following elements are styled by default: **table**, **select**, **headers (h1, h2, and h3)**, **preformatted text (pre)**, **input**, and **text area**.

## Using the describe parameter
<a name="add_custom_widget_describe"></a>

We strongly recommend that you support the **describe** parameter in your functions, even if it just returns an empty string. If you don't support it, and it is called in the custom widget, it displays widget content as if it was documentation.

If you include the **describe** parameter, the Lambda function returns the documentation in Markdown format and does nothing else.

When you create a custom widget in the console, after you select the Lambda function a **Get documentation** button appears. If you choose this button, the function is invoked with the **describe** parameter and the function's documentation is returned. If the documentation is well-formatted in markdown, CloudWatch parses the first entry in the documentation that is surrounded by three single backtick characters (```) in YAML. Then, it automatically populates the documentation in the parameters. The following is an example of such well-formatted documentation. 

```
``` yaml
echo: <h1>Hello world</h1>
```
```

# Security and JavaScript for custom CloudWatch widgets
<a name="add_custom_widget_dashboard_security"></a>

For security reasons, JavaScript is not allowed in the returned HTML. Removing the JavaScript prevents permission escalation issues, where the writer of the Lambda function injects code that could run with higher permissions than the user viewing the widget on the dashboard.

If the returned HTML contains any JavaScript code or other known security vulnerabilities, it is cleaned from the HTML before it is rendered on the dashboard. For example, the **<iframe>** and **<use>** tags are not allowed and are removed.

Custom Widgets won't run by default in a dashboard. Instead, you must explicitly allow a custom widget to run if you trust the Lambda function that it invokes. You can choose to allow it once or allow always, for both individual widgets and entire dashboard. You can also deny permission for individual widgets and the entire dashboard.

# Interactivity in the custom widget in CloudWatch
<a name="add_custom_widget_dashboard_interactivity"></a>

Even though JavaScript is not allowed, there are other ways to allow interactivity with the returned HTML.
+ Any element in the returned HTML can be tagged with special configuration in a `<cwdb-action>` tag, which can display information in pop-ups, ask for confirmation on clicks, and call any Lambda function when that element is chosen. For example, you can define buttons that call any AWS API using a Lambda function. The returned HTML can be set to either replace the existing Lambda widget's content, or display inside a modal.
+ The returned HTML can include links that open new consoles, open other customer pages, or load other dashboards.
+ The HTML can include the `title` attribute for an element, which gives additional information if the user hovers over that element.
+ The element can include CSS selectors, such as `:hover`, which can invoke animations or other CSS effects. You can also show or hide elements in the page.

## <cwdb-action> definition and usage
<a name="add_custom_widget_dashboard_cwdb"></a>

The `<cwdb-action>` element defines a behavior on the immediately previous element. The content of the `<cwdb-action>` is either HTML to display or a JSON block of parameters to pass to a Lambda function.

The following is an example of a `<cwdb-action>` element.

```
<cwdb-action 
     action="call|html" 
     confirmation="message" 
     display="popup|widget" 
     endpoint="<lambda ARN>" 
     event="click|dblclick|mouseenter">  
 
     html | params in JSON
</cwdb-action>
```
+ **action**— Valid values are `call`, which calls a Lambda function, and `html`, which displays any HTML contained within `<cwdb-action>`. The default is `html`.
+ **confirmation**— Displays a confirmation message that must be acknowledged before the action is taken, allowing the customer to cancel.
+ **display**— Valid values are `popup` and `widget`, which replaces the content of the widget itself. The default is `widget`.
+ **endpoint**— The Amazon Resource Name (ARN) of the Lambda function to call. This is required if `action` is `call`.
+ **event**— Defines the event on the previous element that invokes the action. Valid values are `click`, `dblclick`, and `mouseenter`. The `mouseenter` event can be used only in combination with the `html` action. The default is `click`. 

**Examples**

The following is an example of how to use the `<cwdb-action>` tag to create a button that reboots an Amazon EC2 instance using a Lambda function call. It displays the success or failure of the call in a pop-up.

```
<a class="btn">Reboot Instance</a>
<cwdb-action action="call" endpoint="arn:aws:lambda:us-east-1:123456:function:rebootInstance" display="popup">  
       { "instanceId": "i-342389adbfef" }
</cwdb-action>
```

The next example displays more information in a pop-up.

```
<a>Click me for more info in popup</a>
<cwdb-action display="popup"> 
   <h1>Big title</h1>
   More info about <b>something important</b>.
</cwdb-action>
```

This example is a **Next** button that replaces the content of a widget with a call to a Lambda function.

```
<a class="btn btn-primary">Next</a>
<cwdb-action action="call" endpoint="arn:aws:lambda:us-east-1:123456:function:nextPage"> 
   { "pageNum": 2 }
</cwdb-action>
```

# Creating a custom widget for a CloudWatch dashboard
<a name="add_custom_widget_dashboard_create"></a>

To create a custom widget, you can use one of the samples provided by AWS, or you can create your own. The AWS samples include samples in both JavaScript and Python, and are created by a AWS CloudFormation stack. For a list of samples, see [Sample custom widgets for a CloudWatch dashboard](add_custom_widget_samples.md).

**To create a custom widget in a CloudWatch dashboard**

1. Open the CloudWatch console at [https://console.aws.amazon.com/cloudwatch/](https://console.aws.amazon.com/cloudwatch/).

1. In the navigation pane, choose **Dashboards**, and then choose a dashboard.

1. Choose the **\$1** symbol.

1. Choose **Custom widget**.

1. Use one of the following methods:
   + To use a sample custom widget provided by AWS, do the following:

     1. Select the sample in the dropdown box.

        The CloudFormation console launches in a new browser. In the CloudFormation console, do the following:

     1. (Optional) Customize the CloudFormation stack name.

     1. Make selections for any parameters used by the sample.

     1. Select **I acknowledge that AWS CloudFormation might create IAM resources**, and choose **Create stack**.
   + To create your own custom widget provided by AWS, do the following:

     1. Choose **Next**.

     1. Choose to either select your Lambda function from a list, or enter its Amazon Resource Name (ARN). If you select it from a list, also specify the Region where the function is and the version to use.

     1. For **Parameters**, make selections for any parameters used by the function.

     1. Enter a title for the widget.

     1. For **Update on**, configure when the widget should be updated (when the Lambda function should be called again). This can be one or more of the following: **Refresh** to update it when the dashboard auto-refreshes, **Resize** to update it whenever the widget is resized, or **Time Range** to update it whenever the dashboard's time range is adjusted, including when graphs are zoomed into.

     1. If you are satisfied with the preview, choose **Create widget**.

# Sample custom widgets for a CloudWatch dashboard
<a name="add_custom_widget_samples"></a>

AWS provides sample custom widgets in both JavaScript and Python. You can create these sample widgets by using the link for each widget in this list. Alternatively, you can create and customize a widget by using the CloudWatch console. The links in this list open an AWS CloudFormation console and use an CloudFormation quick-create link to create the custom widget.

You can also access the custom widget samples on [GitHub](https://github.com/aws-samples/cloudwatch-custom-widgets-samples).

Following this list, complete examples of the Echo widget are shown for each language.

------
#### [ JavaScript ]

**Sample custom widgets in JavaScript**
+ [ Echo](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetEcho-js&template=customWidgets/customWidgetEcho-js.yaml&param_DoCreateExampleDashboard=Yes) – A basic echoer that you can use to test how HTML appears in a custom widget, without having to write a new widget.
+ [ Hello world](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetHelloWorld-js&template=customWidgets/customWidgetHelloWorld-js.yaml&param_DoCreateExampleDashboard=Yes) – A very basic starter widget.
+ [ Custom widget debugger](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetDebugger-js&template=customWidgets/customWidgetDebugger-js.yaml&param_DoCreateExampleDashboard=Yes) – A debugger widget that displays useful information about the Lambda runtime environment.
+ [ Query CloudWatch Logs Insights](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetLogsInsightsQuery-js&template=customWidgets/customWidgetLogsInsightsQuery-js.yaml&param_DoCreateExampleDashboard=Yes) – Run and edit CloudWatch Logs Insights queries.
+ [ Run Amazon Athena queries](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetAthenaQuery-js&template=customWidgets/customWidgetAthenaQuery-js.yaml&param_DoCreateExampleDashboard=Yes) – Run and edit Athena queries.
+ [ Call AWS API](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetAwsCall-js&template=customWidgets/customWidgetAwsCall-js.yaml&param_DoCreateExampleDashboard=Yes) – Call any read-only AWS API and display the results in JSON format.
+ [ Fast CloudWatch bitmap graph](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetCloudWatchBitmapGraph-js&template=customWidgets/customWidgetCloudWatchBitmapGraph-js.yaml&param_DoCreateExampleDashboard=Yes) – Render CloudWatch graphs using on the server side, for fast display.
+ [ Text widget from CloudWatch dashboard](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetIncludeTextWidget-js&template=customWidgets/customWidgetIncludeTextWidget-js.yaml&param_DoCreateExampleDashboard=Yes) – Displays the first text widget from the specified CloudWatch dashboard.
+ [ CloudWatch metric data as a table](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetCloudWatchMetricDataTable-js&template=customWidgets/customWidgetCloudWatchMetricDataTable-js.yaml&param_DoCreateExampleDashboard=Yes) – Displays raw CloudWatch metric data in a table.
+ [ Amazon EC2 table](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetEc2Table-js&template=customWidgets/customWidgetEc2Table-js.yaml&param_DoCreateExampleDashboard=Yes) – Displays the top EC2 instances by CPU utilization. This widget also includes a Reboot button, which is disabled by default.
+ [AWS CodeDeploy deployments](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetCodeDeploy-js&template=customWidgets/customWidgetCodeDeploy-js.yaml&param_DoCreateExampleDashboard=Yes) – Displays CodeDeploy deployments.
+ [AWS Cost Explorer report](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetCostExplorerReport-js&template=customWidgets/customWidgetCostExplorerReport-js.yaml&param_DoCreateExampleDashboard=Yes) – Displays a report on the cost of each AWS service for a selected time range.
+ [ Display content of external URL](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetFetchURL-js&template=customWidgets/customWidgetFetchURL-js.yaml&param_DoCreateExampleDashboard=Yes) – Displays the content of an externally accessible URL.
+ [ Display an Amazon S3 object](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetS3GetObject-js&template=customWidgets/customWidgetS3GetObject-js.yaml&param_DoCreateExampleDashboard=Yes) – Displays an object in an Amazon S3 bucket in your account.
+ [ Simple SVG pie chart](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetSimplePie-js&template=customWidgets/customWidgetSimplePie-js.yaml&param_DoCreateExampleDashboard=Yes) – Example of a graphical SVG-based widget.

------
#### [ Python ]

**Sample custom widgets in Python**
+ [ Echo](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetEcho-py&template=customWidgets/customWidgetEcho-py.yaml&param_DoCreateExampleDashboard=Yes) – A basic echoer which you can use to test how HTML appears in a custom widget, without having to write a new widget.
+ [ Hello world](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetHelloWorld-py&template=customWidgets/customWidgetHelloWorld-py.yaml&param_DoCreateExampleDashboard=Yes) – A very basic starter widget.
+ [ Custom widget debugger](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetDebugger-py&template=customWidgets/customWidgetDebugger-py.yaml&param_DoCreateExampleDashboard=Yes) – A debugger widget that displays useful information about the Lambda runtime environment.
+ [ Call AWS API](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetAwsCall-py&template=customWidgets/customWidgetAwsCall-py.yaml&param_DoCreateExampleDashboard=Yes) – Call any read-only AWS API and display the results in JSON format.
+  [ Fast CloudWatch bitmap graph](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetCloudWatchBitmapGraph-py&template=customWidgets/customWidgetCloudWatchBitmapGraph-py.yaml&param_DoCreateExampleDashboard=Yes) – Render CloudWatch graphs using on the server side, for fast display.
+  [ Send dashboard snapshot by email](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetEmailDashboardSnapshot-py&template=customWidgets/customWidgetEmailDashboardSnapshot-py.yaml&param_DoCreateExampleDashboard=Yes) – Take a snapshot of the current dashboard and send it to email recipients.
+  [ Send dashboard snapshot to Amazon S3](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetSnapshotDashboardToS3-py&template=customWidgets/customWidgetSnapshotDashboardToS3-py.yaml&param_DoCreateExampleDashboard=Yes) – Take a snapshot of the current dashboard and store it in Amazon S3.
+ [ Text widget from CloudWatch dashboard](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetIncludeTextWidget-py&template=customWidgets/customWidgetIncludeTextWidget-py.yaml&param_DoCreateExampleDashboard=Yes) – Displays the first text widget from the specified CloudWatch dashboard.
+ [ Display content of external URL](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetFetchURL-py&template=customWidgets/customWidgetFetchURL-py.yaml&param_DoCreateExampleDashboard=Yes) – Displays the content of an externally accessible URL.
+ [ RSS reader](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetRssReader-py&template=customWidgets/customWidgetRssReader-py.yaml&param_DoCreateExampleDashboard=Yes) – Displays RSS feeds.
+ [ Display an Amazon S3 object](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetS3GetObject-py&template=customWidgets/customWidgetS3GetObject-py.yaml&param_DoCreateExampleDashboard=Yes) – Displays an object in an Amazon S3 bucket in your account.
+ [ Simple SVG pie chart](https://console.aws.amazon.com/cloudwatch/cfn.js?region=us-east-1&action=create&stackName=customWidgetSimplePie-py&template=customWidgets/customWidgetSimplePie-py.yaml&param_DoCreateExampleDashboard=Yes) – Example of a graphical SVG-based widget.

------

**Echo widget in JavaScript**

The following is the Echo sample widget in JavaScript.

```
const DOCS = `
## Echo
A basic echo script. Anything passed in the \`\`\`echo\`\`\` parameter is returned as the content of the custom widget.
### Widget parameters
Param | Description
---|---
**echo** | The content to echo back
 
### Example parameters
\`\`\` yaml
echo: <h1>Hello world</h1>
\`\`\`
`;
 
exports.handler = async (event) => {
    if (event.describe) {
        return DOCS;   
    }
    
    let widgetContext = JSON.stringify(event.widgetContext, null, 4);
    widgetContext = widgetContext.replace(/</g, '&lt;');
    widgetContext = widgetContext.replace(/>/g, '&gt;');
    
    return `${event.echo || ''}<pre>${widgetContext}</pre>`;
};
```

**Echo widget in Python**

The following is the Echo sample widget in Python.

```
import json
     
DOCS = """
## Echo
A basic echo script. Anything passed in the ```echo``` parameter is returned as the content of the custom widget.
### Widget parameters
Param | Description
---|---
**echo** | The content to echo back
     
### Example parameters
``` yaml
echo: <h1>Hello world</h1>
```"""
 
def lambda_handler(event, context):
    if 'describe' in event:
        return DOCS
        
    echo = event.get('echo', '')
    widgetContext = event.get('widgetContext')
    widgetContext = json.dumps(widgetContext, indent=4)
    widgetContext = widgetContext.replace('<', '&lt;')
    widgetContext = widgetContext.replace('>', '&gt;')
        
    return f'{echo}<pre>{widgetContext}</pre>'
```

**Echo widget in Java**

The following is the Echo sample widget in Java.

```
package example;
 
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
 
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
 
public class Handler implements RequestHandler<Event, String>{
 
  static String DOCS = ""
    + "## Echo\n"
    + "A basic echo script. Anything passed in the ```echo``` parameter is returned as the content of the custom widget.\n"
    + "### Widget parameters\n"
    + "Param | Description\n"
    + "---|---\n"
    + "**echo** | The content to echo back\n\n"
    + "### Example parameters\n"
    + "```yaml\n"
    + "echo: <h1>Hello world</h1>\n"
    + "```\n";
 
  Gson gson = new GsonBuilder().setPrettyPrinting().create();
 
  @Override
  public String handleRequest(Event event, Context context) {
 
    if (event.describe) {
      return DOCS;
    }
     
    return (event.echo != null ? event.echo : "") + "<pre>" + gson.toJson(event.widgetContext) + "</pre>";
  }
}
     
class Event {
 
    public boolean describe;
    public String echo;
    public Object widgetContext;
 
    public Event() {}
 
    public Event(String echo, boolean describe, Object widgetContext) {
        this.describe = describe;
        this.echo = echo;
        this.widgetContext = widgetContext;
    }
}
```