Understanding Axure’s Repeater Widget

Joseph Brick
Apr 22 · 19 min read

Axure creates interactive prototypes. This article applies to Axure versions 7 through 9, and assumes only a basic knowledge of Axure. The product images and sample file in this article are from Axure 8, as Axure 9 is still in beta.


Axure’s repeater widget is often the last thing prototypers learn when approaching the product. And it’s no wonder. You can barely get it to do anything truly functional without using expressions and conditional logic.

That said, the repeater is powerful tool. At the end of this article, you’ll be more able to harness that power than most of your workmates.

Plus, if you don’t already have a handle on them, you’ll have the basics of using expressions and conditional logic in Axure.

What is covered in this article

This article has five examples, ranging from simple to fairly advanced.

Through the examples I’ll describe what exactly is going on when the repeater displays itself, how the OnItemLoad interaction figures into that, and how to take advantage of the repeater row’s various properties that are available via the Item object.

I’ll also provide the basics of using expressions and conditional logic in Axure. Feel free to skim past these topics if you are already comfortable with them.

Lastly, I’ll cover using Axure’s date functions in a repeater.

At the end of the article you’ll find a link to an Axure file containing all examples examined in this article

Anatomy of a repeater

A repeater consists of a set of widgets that’s displayed over and over, either horizontally or vertically. It has two important elements, its dataset and its row template, both discussed below.

The repeater’s dataset

Each repeater has a spreadsheet-like set of rows and columns called a dataset. In it, you can fill columns such as memberName and phoneNumber with row values which you then can access for display in rows of the repeater.

A repeater’s dataset with three rows of data

Even if your prototype doesn’t display dataset values—and there are legitimate scenarios that don’t involve using the dataset’s contents—the dataset dictates how many rows the repeater will display. Unless you’ve applied a filter or pagination to the repeater (topics that this article won’t go into), the repeater will display one row for each row of data in the dataset.

There will be more detail about the repeater’s dataset below, but for now let’s focus on the nature of the repeater row.

The repeater row

When you design a repeater, you are designing just one of its rows. You can think of a row as a template that will be displayed over and over. If you are familiar with Sketch, think of a the repeater row as a symbol whose instances the repeater will display…repeatedly.

Let’s jump into a simple example: a low-fidelity wireframe displaying a member list of an imaginary social club.

Example 1: A low-fidelity member list

The simplest use of the repeater is excellent for producing low-fidelity wireframes. Here’s a lo-fi version of this social club’s member list:

A low-fidelity member list

Let’s look at at the row “template” that defined the display above.

The repeater “template” row in design mode

Here in the repeater’s row there’s an image along with few rectangles fashioned to look like text. Absent Axure’s design-mode interface surrounding it, the row in Axure looks exactly like each row displayed in the browser.

There’s no need to discuss the dataset in this case because the repeater isn’t accessing it to display its values. But it’s important to note that the dataset holds three rows of data, which is why three rows are displayed.

But what if you wanted to display a unique member name for each row? In Axure, that requires code. Which brings us to the reason I chose the simple example above:

Meaning, each row will look exactly like how the row is defined within the repeater. This is unfortunate, but true. So where does this code go?

The OnItemLoad event

Axure sends events to widgets when various things happen. When you click on a widget, Axure sends it an OnClick event, which you can respond to using the OnClick interaction.

In Axure, you use an interaction to respond to an event. Interactions have the same name as the events that they handle.

The repeater receives just three events, OnLoad, OnItemLoad, and OnItemResize. The OnItemLoad event is what we’ll focus on, as it’s the key to changing individual rows in the resulting prototype.

Similar to how the OnClick event is sent to a widget when you click it, the OnItemLoad event is sent to the repeater just before it displays each of its rows. This concept is so crucial to understanding the repeater that I’ll say it again, but bigger.

So if there are three rows to display, the OnItemLoad event will be sent three times, once before each row is displayed.

OnItemLoad provides the opportunity to change the widgets in the repeater’s row before displaying each row.

Example 2: A higher-fidelity member list

To illustrate what happens in OnItemLoad, we’ll be using the same member list example above, but here we’ll display different member name for each row.

A higher-fidelity member list

We’ve defined the “template” repeater row in Axure’s design mode, shown below, which now contains a label called memberNameDisplay. Here it’s filled with x’s simply so we can see where the label is. They’ll be overwritten with the member name by the code we are about to add.

Repeater row with a label called “memberNameDisplay

The repeater will display this row instance once for each of the three names in the memberName column of its dataset, which looks like this:

The OnItemLoad interaction looks like this:

OnItemLoad
Case 1
Set text on memberNameDisplay equal to "[[Item.memberName]]"

I’ll need to provide a little background before explaining the OnItemLoad code above.

Double brackets indicate an expression

Everything that appears between double-brackets is an expression. Look at the Set Text command below:

Set text on label equal to "$[[1 + 1]].00"

Spoiler alert: the label will be set to “$2.00”. Let’s look at how that works.

The “$” and the “.00” are not in double-brackets, so Axure will display them exactly how you typed them in. But Axure evaluates anything inside double-brackets as an expression, and then replaces the expression— brackets and all — for its result.

Thus the text of the label is set to “$2.00” instead of “$[[1 + 1]].00”

The Item object

We saw the expression [[Item.memberName]] above Let’s briefly discuss Item.

Item is an object that provides access to various properties of the repeater’s row instances.

I’ll be using the term “row instances” to distinguish the rows that are displayed in the browser from the row template defined in Axure’s design mode.

Each row displayed in the browser is an instance of the row template, and the Item object provides access to the properties of each instance.

To access a property of Item — and thus, a property of a given row instance — you use what’s called dot notation, connecting the desired property to the Item object by a period.

For example, one such Item property is index. It gives you the number of each row instance, starting at 1. To get index property of the Item object — and thus the index of a given row instance — you’d use dot notation:

Set text on label to [[Item.index]]

Back to the example

We discussed Item.index above, which returns the row number of each row instance.

Likewise, to get the value in a dataset column that corresponds to the a given row instance, you would use the expression [[Item.memberName]] if the column whose value you want is called memberName.

Again, here is our example’s OnItemLoad interaction, where memberNameDisplay is a label inside of the repeater row.

OnItemLoad
Case 1
Set text on memberNameDisplay equal to "[[Item.memberName]]"

So let’s look at what happens, row by row, when the the repeater displays itself.

Row instance 1:

  • Before this row instance is displayed, the OnItemLoad event is sent to the repeater, triggering the OnItemLoad interaction
  • The Set Text command above evaluates [[Item.memberName]]
  • Set Text puts “Susan Jackson” into the memberNameDisplay label of the row instance, since her name is first in the dataset.

Row instance 2:

  • The OnItemLoad interaction is triggered
  • The Set Text command above evaluates [[Item.memberName]]
  • Set Text puts “Carol Wright” into the memberNameDisplay label of the row instance, since her name is second in the dataset.

Row instance 3:

  • The OnItemLoad interaction is triggered
  • The Set Text command above evaluates [[Item.memberName]]
  • Set Text puts “Mary Valdez” into the memberNameDisplay label of the row instance, since her name is third in the dataset.

The whole process above happens each time Axure evaluates the repeater.

What actions cause Axure to evaluate the repeater?

Obviously, when the page loads, the repeater needs to go through the process described above. But there are several commands that will cause the repeater to do the same.

For example, when the Add Rows command adds rows, the OnItemLoad code is applied to every row instance, not just the instances of the newly added rows. Likewise, when you filter a repeater, every row that survives the filter will be updated by the OnItemLoad code.

Here are all repeater commands that cause Axure to re-evaluate the repeater:

  • Add Sort / Remove Sort
  • Add Filter / Remove Filter
  • Set Current Page / Set Items Per Page (used for paginating the repeater)
  • Add Rows / Remove Rows
  • Update Rows

The only repeater commands that don’t trigger the OnItemLoad process is Mark / Unmark Rows.

Example 3: Numbering members

Next, let’s modify the OnItemLoad interaction above to give each member a number, starting at 1.

Adding member numbers to example 2 above

In this example, we’ll use the index property of the Item object, which as we discussed above returns the row number of each row instance.

Now that you know the basics of expressions, the following expression in the Set Text command may need no explanation:

OnItemLoad
Case 1
Set text on contactNameDisplay equal to
"[[Item.index]]. [[Item.contactName]]"

Regardless, I’ll explain the expression, which actually consists of two expressions:

[[Item.index]]. [[Item.contactName]]
  • The first expression, brackets and all, is replaced by the number of the current row instance.
  • The period and the space are outside of brackets, so they will appear as literal text.
  • Finally, the second expression is replaced by the member name.
  • The result of the whole expression for the first row is “1. Susan Jackson”

Example 4: Summing column values

Let’s say one the perks of this imaginary social club is that each member is allowed to keep a monthly tab at the club’s bar. We now want the repeater to show each member’s tab amount, as well as the total amount owed to the bar across all members.

Repeater showing each member’s bar tab and the total for the club

Adding the member’s bar tab to the repeater

First, we’ll add a barTab column to the dataset, which contains the amount that each member owes on their tab. To keep this example simple, the club rounds each member’s bar tab to the nearest dollar.

The repeater’s dataset, now with each member’s bar tab

We’ll also add a label to the repeater row called barTabDisplay which will display the amount owed. Here it’s filled with dollar signs just so we can see where it is. These will be overwritten when OnItemLoad runs.

Repeater row “template” with barTabDisplay label added

Here is the new Set Text command added to OnItemLoad:

OnItemLoad
Case 1
Set text on contactNameDisplay equal to
"[[Item.index]]. [[Item.contactName]]"
Set text on barTabDisplay equal to
"Bar tab: $[[Item.barTab]].00"

Notice that “Bar tab: $” sits to the left of [[Item.barTab]] and “.00” sits to the right, so they show up as literal text.

Here is now what the repeater looks like when displayed in the browser:

Looking good, repeater!

Summing all members’ bar tabs

Above we used Item.barTab to display each members tab. Here we’ll be using it to sum all the bar tabs up in a global variable called v_total, which we’ll give the initial value of 0.

The variable for summing up the the club’s total outstanding tab, initialized to 0

Here’s the expression to OnItemLoad that will do the summing.

Set value of variable v_total to [[v_total + Item.barTab]]

Don’t worry if this doesn’t make sense to you. It will momentarily. Meanwhile, here the current OnItemLoad code:

OnItemLoad
Case 1
Set text on contactNameDisplay equal to
"[[Item.index]]. [[Item.contactName]]"
Set text on barTabDisplay equal to
"Bar tab: $[[Item.barTab]].00"
Set value of v_total equal to "[[v_total + Item.barTab]]"

Now let’s what sees what happens, row by row, as the repeater displays each of its row instances:

Row instance 1

  • The value of the global variable v_total is 0
  • The OnItemLoad message is sent to the repeater
  • The two Set Text commands update Susan’s name and $70 bar tab
  • The Set Variable command sets v_total to [[v_total + Item.barTab]]. Since v_total is 0 and Item.barTab is 70, v_total is set to 0 + 70

Row instance 2

  • The value of v_total is now 70
  • The OnItemLoad message is sent to the repeater
  • The two Set Text commands update Carol’s name and $30 bar tab
  • [[v_total + Item.barTab]] is now 70 + 30, so v_total gets that amount

Row instance 3

  • The value of variable v_total is now 100
  • The OnItemLoad message is sent to the repeater
  • The two Set Text commands update Mary’s name and $50 bar tab
  • [[v_total + Item.barTab]] is now 100 + 50, so v_total gets that amount

So great! We now have the bar’s 150 dollar tab total. But it’s not displayed anywhere: it’s just sitting in a global variable. To display the total we’ll do three things:

  • Place a label called barTabTotal below the repeater. (Don’t put it inside of the repeater — otherwise, it will be repeated on the screen three times.)
  • Put the value of v_total into that label once all rows have been summed.
  • Reset v_total to 0.

Why reset v_total to 0?

Remember, there are a number of commands that will cause the repeater to be re-evaluated, causing OnItemLoad to run again for every row. Adding a new member using the Add Rows command would cause that to happen.

In such a case, if we didn’t reset v_total to 0, it would start out at 150 from the previous OnItemLoad process, and the new sum would end up $150 too high.

Using conditional logic to display the total and reset v_total

So where do we put the code that places the sum into the label and zeroes out v_total? We can’t do that every time the OnItemLoad message is received, because that will zero out v_total each time a row instance is displayed. We want to wait until the repeater receives the OnItemLoad message for the last row instance.

Here we will use a condition, testing if the current row instance is the last row of the repeater. If you aren’t familiar with conditional logic in Axure, you should read this help article created by the Axure team. In the meantime, I’ll walk you through it.

Adding a second case to the OnItemLoad interaction

Thus far we have a single case, Case 1, that executes on every row instance:

OnItemLoad
Case 1
Set text on contactNameDisplay equal to
"[[Item.index]]. [[Item.contactName]]"
Set text on barTabDisplay equal to
"Bar tab: $[[Item.barTab]].00"
Set value of v_total equal to "[[v_total + Item.barTab]]"

We now want to add a second case that executes on the last row only. To add a case to an interaction, select the interaction and click the Add Case button:

Alternatively, you can simply double-click the name of the interaction to get a new case. Either method opens the case editor with an empty Case 2:

Creating Case 2 for this example

Since we want Case 2 to act upon the last row only, we’ll create a condition by pressing at the Add Condition button. We’ll use the Item property isLast, which evaluates to TRUE only when the OnItemLoad interaction is acting upon the last row instance.

The condition above says…

[[Item.isLast]] equals value "true"

…which will cause Case 2 to execute only once OnItemLoad hits the last row of the repeater. Exactly what we want.

Here we’ll click OK in the case editor. After adding the commands to display the total and zero out the v_total variable, we end up with this:

OnItemLoad
Case 1
(If True)
Set text on contactNameDisplay equal to
"[[Item.index]]. [[Item.contactName]]"
Set text on barTabDisplay equal to
"Bar tab: $[[Item.barTab]].00"
Set value of v_total equal to "[[v_total + Item.barTab]]"
Case 2
(Else if [[Item.isLast equals "true"]])
Set text on barTabTotal equal to
"Club bar tab: $[[v_total]].00"
Set value of v_total to "0"

Now when we display the repeater in the browser, we get this:

Um… why isn’t the total displaying?

You’ll see immediately that there’s a problem. The total value wasn’t updated. Let’s look at the OnItemLoad code to see why:

Case 1 says (If True), meaning it will always execute, no matter what.

But Axure applied (Else If…) to Case 2. An Else If executes only when the previous condition didn’t execute. Thus, Case 2 will never execute unless we change it from an Else If to an If.

I wish Axure didn’t default to an Else If in this situation. It’s not what the prototyper would ever want when the first case always executes.

You can toggle If and Else If by right-clicking it and choosing Toggle IF/ELSE IF.

Toggle an Else IF to change it to an If and vice-versa

Doing so we end up with the following:

OnItemLoad
Case 1
(If True)
Set text on contactNameDisplay equal to
"[[Item.index]]. [[Item.contactName]]"
Set text on barTabDisplay equal to
"Bar tab: $[[Item.barTab]].00"
Set value of v_total equal to "[[v_total + Item.barTab]]"
Case 2
(If [[Item.isLast equals "true"]])
Set text on barTabTotal equal to
"Club bar tab: $[[v_total]].00"
Set value of v_total to "0"

Once again, let’s walk through the the OnItemLoad interaction for each row instance.

Row instance 1

  • The OnItemLoad message is sent to the repeater.
  • Susan’s information is displayed (see examples 2 and 3 above).
  • v_total is upped to 70 by Susan’s $70 bar tab.
  • This is not the last row. Case 2 is ignored because [[Item.isLast]] is FALSE.

Row instance 2

  • The OnItemLoad message is sent to the repeater.
  • Carol’s information is displayed
  • v_total is upped to 100 by Susan’s bar $30 tab.
  • This is not the last row. Case 2 is ignored because [[Item.isLast]] is FALSE.

Row instance 3

  • The OnItemLoad message is sent to the repeater.
  • Mary’s information is displayed.
  • v_total is upped to 150 by Mary’s bar $50 tab.
  • Case 2 is executed because [[Item.isLast]] is TRUE. Case 2 then sets the barTabTotal label to the total tab amount and resets v_total to 0.

This gives us the correct display:

The corrected output

Example 5: Keeping dates fresh

This example is a bit more advanced than the ones above, so I’ll walk through concepts may that may be new to you.

I often can tell when I created a prototype by the dates displayed in it. If I see sample text with the date 12/1/2015, it’s a tell-tale sign that the prototype hasn’t been updated since then. But it doesn’t have to be that way.

Here we’ll add a column to the repeater’s dataset called daysAgo, containing how many days ago each member paid their annual dues. We’ll then convert this to a date when we display it.

Here is the repeater’s dataset along with the repeater’s output, assuming the current date is April 19, 2019.

Dataset with column showing days since a member’s dues were paid
Prototype when run on April 19, 2019

To achieve the above we’ll use a few of Axure’s date functions. Axure has all sorts of functions — for manipulating strings, for examining widgets, and yes, for messing with dates — and they’re all listed in what I call the fx box.

Exploring available functions in the fx box

Whenever you see the fx icon next to a field Axure’s interface, clicking it brings up the dialog box shown below.

This allows you to enter lengthy expressions into a large field as well as define local variables, which I won’t be covering in this article.

There is another useful feature here, hidden behind the Insert Variable or Function button: a list of all functions that Axure supports. Here are the date functions alone.

This little gem contains a listing of all available functions

These functions are largely identical to Javascript functions of the same name, so to find out how one works, just Google the function name followed by “javascript.”

Note that dates are stored as the number of milliseconds offset from an arbitrary date in the 1970s, so dates also contain time. This is why you see time functions in the listing above.

The Now object

You’ll see at the top of the date-function list above the entry Now. This is a date object that contains the current date and time.

If you were to execute the following command at 3:24 pm on April 19th, 2019 while seated in California…

Set text on label to "[[Now]]"

…Axure would display this:

Fri Apr 19 2019 15:24:00 GMT-0700 (Pacific Daylight Time)

This is the default date format Axure returns when you display a string value from a date object. Not the easiest format to read — though certainly easier than trillions of milliseconds — but there are functions in the list above that allow us to extract more user-readable values.

Almost all of the date functions in the list above either extract information from a date object or create a new date object. That may sound nonsensical at this point, but I’ll discuss each of the functions we’ll be using in this example.

The addDays() function

This addDays() function creates a new date object from an existing date object. It expects a number in its parentheses that represents the number of days this new date object will be offset from the existing one. The function properly crosses month and year boundaries and accounts for leap years.

Say today is April 19, 2019. If you were to say…

[[ Now.addDays(1) ]]

…the above would return a new date object representing the date 4/20/2019. Likewise, Now.addDays(-1) would return the a date object representing the date 4/18/2019.

To make things more legible, I’m adding spaces between the expression and the enclosing brackets. Axure ignores leading and trailing spaces inside of brackets.

The getMonthName() function

The getMonthName() function extracts a the month name from a date object. So assuming today is April 19, 2019, Now.getMonthName() would give us “April”. Note that this function doesn’t expect any value in its parentheses.

The getDate() and getFullYear() functions

Just as getMonthName() extracts the month’s name from a date object, getDate() extracts the day of the month (e.g., 19) from a date object, and getFullYear() extracts the four-digit year from the same.

Be careful not to confuse getDate() with getDay(), which returns the day of the week in numeric form, with 0 being Sunday and 6 being Saturday.

Chaining functions together

Axure, like Javascript, allows you to take the output of one function and pass it through another function. This is called “chaining.” Let’s look at examples of chaining.

  • Now is a date object containing today’s date.
  • Now.addDays(1) creates a new date object containing tomorrow’s date.
  • Now.addDays(1).getDayOfWeek() gives us the weekday from the date object representing tomorrow’s date: e.g., “Saturday” if today is Friday.
  • Now.addDays(1).getDayOfWeek().substring(0, 3) gives us the first three characters of tomorrow’s weekday : “Fri” if today is Friday. (Google “substring javascript” if you’re curious what substring() does.)

We will be using such chaining to construct the dates for this example.

Back to the example

In case you don’t want to scroll up, here again is what we are trying to achieve:

The same repeater output shown above

And here again is the repeater’s the dataset:

The same dataset shown above

Updating the repeater row

As before, we will be adding a label to the repeater row “template” so we have a place to put the dues-paid date:

The row template with the datePaidDisplay label added

The label is called datePaidDisplay and it contains a date-looking value just so we can see where it is. This value will be overwritten by the Set Text command that we’ll add to Case 1 of the OnItemLoad interaction.

Using date expressions in the Set Text command

Below are the expressions we’ll use to extract each part of the date from the daysAgo column. At the end we’ll assemble these into a single expression and place the result in the repeater’s datePaidDisplay label.

Getting the month from Item.daysAgo

Here is the expression that returns the desired month from the number in the daysAgo column. Refer to the function descriptions above if needed.

[[ Now.addDays(-Item.daysAgo).getMonthName() ]]

Remember that addDays() takes a number that tells it how many days to add. Item.daysAgo grabs the number from the current row’s daysAgo column and makes it negative.

In the first row instance, Susan Jackson’s daysAgo value is 1, so when OnItemLoad runs for the that row instance, the following is evaluated:

[[ Now.addDays(-1).getMonthName() ]]

The expression above creates a new date object with yesterday’s date and extracts the month name from it.

Getting the day of the month from Item.daysAgo

This follows the same pattern as above, using getDate():

[[ Now.addDays(-Item.daysAgo).getDate() ]]

…which for the first row Axure interprets as:

[[ Now.addDays(-1).getDate() ]] 

Getting the four-digit year from Item.daysAgo

This follows the same pattern as above, extracting the four-digit year:

[[ Now.addDays(-Item.daysAgo).getFullYear() ]]

Combining all of the above into a single expression

As you’ve seen before, we’ll be combining these expression into a single expression, placing spaces and commas between the expressions as necessary to achieve the desired date format.

Set text on datePaidDisplay to 
[[ Now.addDays(-Item.daysAgo).getMonthName() ]]
[[ Now.addDays(-Item.daysAgo).getDate() ]],
[[ Now.addDays(-Item.daysAgo).getFullYear() ]]

You can’t see them, but there are spaces at the end of the first two expressions, which gives us the spaces we need between the date elements.

Here is the whole OnItemLoad interaction:

OnItemLoad
Case 1
(If True)
Set text on contactNameDisplay equal to
"[[Item.index]]. [[Item.contactName]]"
Set text on barTabDisplay equal to
"Bar tab: $[[Item.barTab]].00"
Set text on datePaidDisplay to
[[ Now.addDays(-Item.daysAgo).getMonthName() ]]
[[ Now.addDays(-Item.daysAgo).getDate() ]],
[[ Now.addDays(-Item.daysAgo).getFullYear() ]]
Set value of v_total equal to "[[v_total + Item.barTab]]"
Case 2
(If [[Item.isLast equals "true"]])
Set text on barTabTotal equal to
"Club bar tab: $[[v_total]].00"
Set value of v_total to "0"

And here is the final output:

Result of the OnItemLoad interaction above

Sample file and final notes

As promised, here is the sample file that contains all five examples from this article.

If you have any questions, or if any of the topics weren’t presented clearly enough, please leave a comment below. Also, let me know if there are other Axure topics you’d like to see covered, repeater-related or not.


Joseph Brick is a UX Designer and prototyper living in Seattle, Washington. You can find him on the Axure forums, where his member name is josephxbrick.

Joseph Brick

Written by

Joseph Brick is a UX designer and prototyper living in Seattle, Washington.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade