Creating an Accordion Design: Part III

10m

Intermediate

Getting started

We’ll be making this project with Framer. If you don’t have it already, download a free 14-day trial.

Recap

Welcome to the final part of this series on creating an accordion design. Interactive cards like the ones we are making often contain information based on real data — think for instance of your favorite music player or flight booking app. These should contain real data about actual songs and albums or ticket prices and destinations.

Ideally, we’d also want this to be the case in the prototyping phase, especially if you’re user testing and want to evoke real human interactions and emotions for instance when you are running user tests.

This final part will revolve around doing just that—getting our cards hooked up and mapping over real data from a JSON file.

We will look at how we can overwrite the text values in our design component, and consequently how to replace these values with our real data.

Setting up a JSON file

Whenever you’re working with real data, you will often work with JSON files. These allow us to store objects with data contained in each object. Framer allows us to work with JSON files from within our code editor, so let’s get started by creating a new JSON file.

In Framer, open your code editor from the lefthand menu. After clicking New, expect a prompt to name your new file. Make sure to type .json at the end of the name and Framer will recognize you’re creating a JSON file instead of a code component.

Here is where we can store our data as if it’s coming from an actual data source. JSON objects are set up in key: value pairs, which allows us to assign new values if we use the correct keys.

The example below, for instance, contains a general data object cardData with various data objects that each contain a key and a value. An example of a key would be song, which has the value of "The Little Things".

{
"cardData": [
{
"song": "The Little Things",
"duration": "4:06",
"album": "Chinatown",
"image": "https://bit.ly/3bkrytS",
"year": "2019"
},
{
"song": "Midnight Cowboy",
"duration": "2:59",
"album": "Values",
"image": "https://bit.ly/38anBWO",
"year": "2019"
},
{
"song": "Big Calm",
"duration": "4:26",
"album": "Gut Feeling",
"image": "https://bit.ly/374ktdG",
"year": "2019"
}
]
}
COPY

Implementing the JSON file

Importing our card data

Let’s head back to our code component file. At the top, below our other imports, we want to tell React that we’re going to be using a JSON file in our code component.

import { cardData } from "./data.json"
COPY

Mapping over our JSON

We now want to map over our array of data objects in our data.json file, whereas in Part II we mapped over an array that we artificially added to our defaultProps. This means we need to change our .map method logic to map over cardData instead of props.cards.

{cardData.map(card => {
return
<Card key={card} />
})}
COPY

We can now safely remove our array of cards: ["cardOne", "cardTwo", "cardThree"] from defaultProps, as we are using a much more useful approach now.

Updating our key

In the second part we said that React requires a unique key value. Card is not unique anymore, as it returns an entire object for all cards. Let’s point the key to a more unique property, like our song name. We do this by setting our key to equal card.song instead:

{cardData.map(card => {
return
<Card key={card.song} />
})}
COPY

Overriding a design component

At the very start, we created a design component that would function as our card. One great thing about design components is that we can expose the properties in them (such as text values or images) and override those later.

To override these from code, we need to name these layers so we can easily identify them. Navigate back to the canvas, find your master design component, and take a look at the properties panel on the right of the Framer interface.

The moment you turned your card into a design component, Framer will provide you a list of the text layers and images that it found in your design component. You can tick off each one that you want to be able to override in any future design component’s instance you create.

Overriding instances

You may know this feature from design components on the canvas. However, this doesn’t just work with instances on the canvas, but also from code. This is because when we render our design component in our code component, we are essentially creating instances also.

In the design component created on the canvas, I’ve named the values of the text layers and image that I want to override later on song, album, duration, year, and image.

If we now tick these off in the properties panel, we allow Framer to override them later on, allowing us to add custom texts and images.

Overriding text values

If we now return to our code component and take a look at the body of our <Card> component, we can write the exact name of each text layer (and image) as previously assigned in our design component, and override it with whatever we want by assigning a value to it (e.g. song="The Little Things").

However, you may remember from Part II that the method we used required to have the card parameter defined when we wrote cardData.map(card. This is now extra useful, as card will give us all of the data within our data.json file objects because we are now mapping over those instead.

We can use the data we receive to override these text layers. We do this by adding the properties of our design component that we want to override and assigning the value we find in our JSON file after mapping over each card. For instance, we want to override the song text field with the value that’s the equivalently named song property from our JSON file.

<Card
key={card.song}
overflow="hidden"
variants={cardVariants}
onTap={() => setIsOpen(isOpen === card ? null : card)}
animate={isOpen === card ? "open" : "closed"}
initial={false}
song={card.song}
album={card.album}
duration={card.duration}
year={card.year}
image={card.image}
/>
COPY

Finished work

Congratulations! We’ve finished creating our accordion design that even behaves as if it’s consuming real data from a JSON file, and on top of that we’ve learned how to override design component props.

The code for our finished product will look something like this:

import * as React from "react"
import { Stack } from "framer"
import { Card } from "./canvas"
import { cardData } from "./data.json"
export function Accordion(props) {
const [isOpen, setIsOpen] = React.useState(null)
const cardVariants = {
open: {
transition: {
type: "spring",
delay: 0,
stiffness: 250,
damping: 48,
mass: 3,
duration: 0.8,
},
height: "174px",
},
closed: {
transition: {
type: "tween",
duration: 0.3,
},
height: "98px",
},
}
return (
<Stack gap={10} alignment="start" distribution="start">
{cardData.map(card => {
return (
<Card
key={card.song}
overflow="hidden"
variants={cardVariants}
onTap={() => setIsOpen(isOpen === card ? null : card)}
initial={false}
animate={isOpen === card ? "open" : "closed"}
song={card.song}
album={card.album}
duration={card.duration}
year={card.year}
image={card.image}
/>
)
})}
</Stack>
)
}
Accordion.defaultProps = {
width: 366,
height: 174,
}
COPY

Review

Download the finished project for part III ›

If you’re interested in having your prototype pull in production data from a real API, I recommend reading Zach Johnston’s article on designing with APIs.

Got stuck before the finish line somewhere? Feel free to reach out by sending me a tweet. If you’d rather start a discussion head on over to our communities at Spectrum or Slack. Of course, you can also contact support directly.