I think you will find your answer in [this blog from Jonathan Robbins][1].

It is quite some code - too much to copy here.
An overview of what is all in his example:

 - SubPageCode rendering
 - The `Border` rendering adds a Placeholder for the other renderings.
 - The `MessageBar` is used to display messages
 - `AdvancedExpander` datasources the `SampleOrdersAccordion` Item we created earlier. The next four renderings are added to this AdvancedExpander‘s placeholder.
 - The `ListControl` is responsible for displaying the data
  - Set the `Datasource` the `SampleOrders ListControl` item
  - Items field looks at the data provider to know what to display e.g. `{Binding SampleOrdersDataProvider.cidata}`. 
 - `ProgressIndicator` displays a loading indicator for the `ListControl`
  - Set it’s `Target` property to the `Id` of the `ListControl`
  - `IsBusy` property is wired to the following `GenericDataProvider` e.g. `{Binding SampleOrdersDataProvider.IsBusy}` 
  - Finally it datasourced to the out-of-the-box `ProgressIndicator` Item.
 - `GenericDataProvider` actually holds the data we are loading and displaying. Datasourced to an Item called `DataProviderForListControl` – `{6CD06869-4264-420C-A161-3220DD3AB7BA}`
 - `Border` adds a placeholder for a ‘see more’ button is more data is available than the page size.
  - To set this conditional visibility set the `IsVisible` field to a property of the `DataProvider` denoting if there is more data available than the page – `{Binding SampleOrdersDataProvider.HasMoreData}`
 - The `Button` facilitates pagination of the data, its `Click` event field set to a preexisting JavaScript Method – `javascript:app.SampleOrdersDataProvider.nextListData()`.


His result looks like this:
[![enter image description here][2]][2]


  [1]: https://jonathanrobbins.co.uk/2016/04/19/extending-sitecore-experience-profile-speak-app/
  [2]: https://i.sstatic.net/jlgFe.png