Add search and filter to your app
Add search and filter functionality to an app built with the Miro Web SDK.
The Miro Developer Platform allows developers to create apps and integrations on top of Miro that add extra or custom functionality.
Whether it's something creative, useful, or something that allows you and your team to collaborate more efficiently together, the Miro Developer Platform gives you all the pieces you need to build your next big idea.
Goal
In this guide, we’ll be exploring how to implement asset search in your Web SDK-based app.
The sample app we're going to create together builds on the finished Add drag and drop to your app tutorial.
Download the complete code sample for this guide from our app-examples repository.
Here's what we’ll build:
Add a search input
Path: src/components/Input.tsx
To search through our assets, we need an input that can do a few things:
- Accept user input.
- Pass the input value to our image list, so we can filter it.
- Clear the input value upon clicking.
(We’ll add a ✖ close icon to perform this action.)
All the styles for our component are already included in styles.css
.
Track internal state
const [internalInputValue, setInternalInputValue] = useState("");
This line of the Input.tsx
file does the following:
- It creates a variable, called
internalInputValue
, to hold the state of the input. - It creates a function, called
setInternalInputValue
, to update the value of this state.
We’ll call the second function every time the input changes, and we'll update the first variable with the current input value.
We need to keep track of this value, so we can clear the value if users click the ✖ close icon.
We’ll explore this step in the next section.
Clear internal state
Now that we are keeping track of the current value of the input, we’re also able to clear it if a user initiates the action.
We created a separate component called X
for the ✖ close icon.
const X = ({ onClick }: { onClick: () => void }) => {
return (
<div id={"input-x-container"} onClick={onClick}>
<svg
width="12"
height="12"
viewBox="0 0 12 12"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M11.1382 1.8047C11.3985 1.54435 11.3985 1.12224 11.1382 0.861888C10.8778 0.601539 10.4557 0.601539 10.1953 0.861888L6.00008 5.05715L1.80482 0.861888C1.54447 0.601539 1.12236 0.601539 0.862011 0.861888C0.601661 1.12224 0.601661 1.54435 0.862011 1.8047L5.05727 5.99996L0.862011 10.1952C0.601661 10.4556 0.601661 10.8777 0.86201 11.138C1.12236 11.3984 1.54447 11.3984 1.80482 11.138L6.00008 6.94277L10.1953 11.138C10.4557 11.3984 10.8778 11.3984 11.1382 11.138C11.3985 10.8777 11.3985 10.4556 11.1382 10.1952L6.94289 5.99996L11.1382 1.8047Z"
fill="#5F5C80"
/>
</svg>
</div>
);
};
We’re giving X
a property called onClick
. This property fires when users click the ✖ close icon.
After adding the X
component to the Input
component above, we can pass the onClick
property a function that will clear the current state of the input. The function name is handleClearInput
.
In this function, we call the function to set the internal input value, and we clear the state by setting its value to “”
.
We’re only showing the X component if the value is not empty, so we will only see it after the user starts to type.
{internalInputValue !== "" && <X onClick={handleClearInput} />}
The handleClearInput
function contains a call to handleInputChange
. This is a render prop; we’ll discuss it in the next section.
Pass the input value to a parent component through render props
Because we’re keeping track of the input value only internally, we also need a way to access this value from a parent component; in our case, the main app.
In React, this pattern is known as using render props.
This pattern is the same as creating a prop for our component; but in this case, our prop is a function.
At the beginning of the input.tsx
file, we define the handleInputChange
render prop, so we can use it further down in our component.
const Input = ({
handleInputChange,
}: {
handleInputChange: (e: string) => void;
}) => {
...
We call this function every time the input changes, and when the input value is cleared. This passes the current value of our input to the parent when we call this render prop.
Import input into app and track value in app state
Path: src/App.tsx
Now that we have a working Input, we can use it inside our main app.
We import our new component in App.tsx
, and then we render it at the top of our app container.
...
return (
<div className="main">
<Input handleInputChange={(value) => setInputValue(value)} />
...
We're also calling our handleInputChange
prop, and setting the state of our app with the value returned from the Input.
We're keeping track of this value on in a variable called inputValue
.
const [inputValue, setInputValue] = useState("");
We now have a component that collects user input, and a variable that contains its current value!
Add names and tags to images
Our images currently don’t have any information attached to them, other than the URL we’re using to insert the image.
To filter our images by the keyword we type into the input, we’ll need to add some extra properties to the image data by converting the array of image strings into an array of image objects.
For this example, we’ve turned the strings into objects, and we've updated the object's keys to include url
, name
, and tags
.
- The
url
key remains the same as the original URL string that the image had. - The
name
key can be something descriptive about the image being rendered. - The
tags
key can be an array containing multiple strings; each string should be descriptive and/or related to the image.
const images = [
{
url: "https://static-website.miro.com/static/images/page/mr-features-1/tour-m-projects.svg",
name: "projects",
tags: ["folders", "collaboration"],
},
{
url: "https://static-website.miro.com/static/images/page/mr-features-1/tour-m-account.svg",
name: "account",
tags: ["user", "profile"],
},
{
url: "https://static-website.miro.com/static/images/page/mr-features-1/tour-m-product.svg",
name: "product",
tags: ["tool", "collaboration"],
},
{
url: "https://static-website.miro.com/static/images/page/mr-features-1/tour-m-ux-research.svg",
name: "ux research",
tags: ["design", "information"],
},
{
url: "https://static-website.miro.com/static/images/page/mr-features-1/tour-m-learn.svg",
name: "learn",
tags: ["education", "tutorials"],
},
];
After updating the image array, we also need to make sure we update the mapped image in our return body.
To do so, we pass the url
to the img
tag src
, so our app can render correctly.
Add a filter
Now that we have an input to collect our search term and an array of images with metadata, all we need to do is add a filter that returns the image we’re looking for.
Luckily, JavaScript includes a helpful method we can use to filter items from an array.
In App.tsx
, we’re setting up our filter like this:
.filter((o) => {
return (
o["name"].toLowerCase().includes(inputValue.toLowerCase()) ||
o["tags"].some((value) => {
return value.includes(inputValue.toLowerCase());
})
);
})
The filter contains 2 parts: one that filters the list of images by name
, and the second that filters it by tags
.
We can continue this pattern if we have more fields that we want to filter our images with; for example, we could add a new property to the image data called size, or author.
Let's wrap up
Implementing a search functionality to your app can be as complex as you need it to be, but this should serve as a simple example on implementing it in the context of images that might have multiple properties you would like to filter by.
Updated about 1 year ago
To discover and to get acquainted with more Web SDK features, check the other tutorials.