Learn more about our current job openings and benefits of working at FSL.
Detailed reviews and feedback from past and current clients.
Get to know the Management Team behind FullStack Labs.
Our step-by-step process for designing and developing new applications.
Writings from our team on technology, design, and business.
Get answers to the questions most frequently asked by new clients.
Learn about our company culture and defining principles.
A high level overview of FullStack Labs, who we are, and what we do.
A JavaScript framework that allows rapid development of native Android and IOS apps.
A JavaScript framework maintained by Facebook that's ideal for building complex, modern user interfaces within single page web apps.
A server side programming language known for its ease of use and speed of development.
A lightweight and efficient backend javascript framework for web apps.
An interpreted high-level programming language great for general purpose programming.
A JavaScript framework maintained by Google that addresses many of the challenges encountered when building single-page apps.
A JavaScript framework that allows developers to build large, complex, scalable single-page web applications.
A progressive JavaScript framework known for its approachability, versatility, and performance.
A progressive JavaScript framework known for its approachability, versatility, and performance.
A progressive JavaScript framework known for its approachability, versatility, and performance.
A progressive JavaScript framework known for its approachability, versatility, and performance.
A progressive JavaScript framework known for its approachability, versatility, and performance.
A progressive JavaScript framework known for its approachability, versatility, and performance.
A progressive JavaScript framework known for its approachability, versatility, and performance.
View a sampling of our work implemented using a variety of our favorite technologies.
View examples of the process we use to build custom software solutions for our clients.
View projects implemented using this javascript framework ideal for building complex, modern user interfaces within single page web apps.
View projects implemented using this framework that allows rapid development of native Android and IOS apps.
View projects implemented using this backend javascript framework for web apps.
View projects implemented using this high-level programming language great for general purpose programming.
View projects implemented using this server side programming language known for its ease of use and speed of development.
We have vast experience crafting healthcare software development solutions, including UI/UX Design, Application Development, Legacy Healthcare Systems, and Team Augmentation. Our development services help the healthcare industry by enhancing accessibility, productivity, portability, and scalability.
We offer a range of custom software development solutions for education companies of all sizes. We're experts in Education Software Development and specialists in enhancing the learning experience across web, mobile, and conversational UI.
We're experts in developing Custom Software Solutions for the Logistics Industry. Our work offered a whole new and more efficient way for Logistics companies to manage their crucial operations.
We partner with various construction industry organizations to build custom software development solutions. Our Construction Software Development Services allow construction companies to manage projects, resources, and documentation.
We have vast experience crafting healthcare software development solutions, including UI/UX Design, Application Development, Legacy Healthcare Systems, and Team Augmentation. Our development services help the healthcare industry by enhancing accessibility, productivity, portability, and scalability.
We offer a range of custom software development solutions for education companies of all sizes. We're experts in Education Software Development and specialists in enhancing the learning experience across web, mobile, and conversational UI.
We're experts in developing Custom Software Solutions for the Logistics Industry. Our work offered a whole new and more efficient way for Logistics companies to manage their crucial operations.
We partner with various construction industry organizations to build custom software development solutions. Our Construction Software Development Services allow construction companies to manage projects, resources, and documentation.
Learn more about our current job openings and benefits of working at FSL.
Detailed reviews and feedback from past and current clients.
Get to know the Management Team behind FullStack Labs.
Our step-by-step process for designing and developing new applications.
Writings from our team on technology, design, and business.
Get answers to the questions most frequently asked by new clients.
Learn about our company culture and defining principles.
A high level overview of FullStack Labs, who we are, and what we do.
A JavaScript framework that allows rapid development of native Android and IOS apps.
A JavaScript framework maintained by Facebook that's ideal for building complex, modern user interfaces within single page web apps.
A server side programming language known for its ease of use and speed of development.
A lightweight and efficient backend javascript framework for web apps.
An interpreted high-level programming language great for general purpose programming.
A JavaScript framework maintained by Google that addresses many of the challenges encountered when building single-page apps.
A JavaScript framework that allows developers to build large, complex, scalable single-page web applications.
A progressive JavaScript framework known for its approachability, versatility, and performance.
A dynamic programming language used in all sorts of web and mobile applications.
A cross-platform programming language designed to run robust applications on any device.
A UI toolkit used to build natively compiled applications from a single codebase.
A functional programming language that’s ideal for scalability, maintainability, and reliability.
A Customer Relationship Management (CRM) platform that seamlessly integrates with your business operations.
A high-performance programming language that makes it easy to build simple, reliable, and efficient software.
View a sampling of our work implemented using a variety of our favorite technologies.
View examples of the process we use to build custom software solutions for our clients.
View projects implemented using this javascript framework ideal for building complex, modern user interfaces within single page web apps.
View projects implemented using this framework that allows rapid development of native Android and IOS apps.
View projects implemented using this backend javascript framework for web apps.
View projects implemented using this high-level programming language great for general purpose programming.
View projects implemented using this server side programming language known for its ease of use and speed of development.
As users come to expect the same quality of UI from websites as in native applications, it’s a good idea to implement some native patterns that are common in native UIs. However, some patterns that work well for desktop applications don’t necessarily work for mobile, and vice versa.
This can pose a challenge when building responsive web applications that have to take into account multiple layouts and screen widths. Today, we’ll implement a filter component that adapts not only its layout but also its behavior according to the device’s screen size to provide a UI that feels comfortable on both mobile and desktop devices.
Let's start with a mockup of the component to implement. The idea is to provide users with a button that, when active, will display a dropdown on desktop and a fullscreen modal on mobile:
In order to achieve this, we’ll implement a single component that will, depending on the screen, display one component or the other.
The first step will be implementing a button that will hold the state of whether the filter is open or not. We’ll also apply some basic styling so that our button looks good:
-- CODE language-jsx keep-markup --
/* Filter.js */
import React, { useState } from "react";
import "./filter.css";
default function Filter() {
const [isOpen, setIsOpen] = useState(false);
return (
<div classname="filter"></div>
<button onclick="{()" ==""> setIsOpen(!isOpen)} className="filter__button"></button>
Technologies
);
}
Our button looks pretty good, but it doesn’t do much. Let’s fix that by showing a drop-down whenever the isOpen state is set to true and adding the button in charge of applying the filter:
-- CODE language-jsx keep-markup --
<div classname="filter"></div>
<button onclick="{()" ==""> setIsOpen(!isOpen)} className="filter__button"></button>
Technologies
{isOpen && (
<div ref="{dropdownRef}" classname="filter__dropdown"></div>
<div style="{{" paddingtop:="" "2rem",="" paddingbottom:="" "2rem"="" }}=""></div>
{`Dropdown content goes here `}
<div classname="filter__dropdown__actions"></div>
<button onclick="{handleApply}" classname="filter__dropdown_button"></button>
Apply
)}
We need to make sure that the dropdown stays hidden on smaller screens. There’s a couple of ways we could do this, but to keep it simple we’ll use a media query and hide the dropdown on smaller screens:
There’s one last piece left in our dropdown implementation. Whenever a user clicks outside of the floating dropdown, they’d expect the modal to close. In order to implement that, we’ll have to enhance our component with a couple of Refs and the useEffect hook:
This will prevent the dropdown from unexpectedly closing while the user is interacting with the filter.
Protip: You could further refactor this useEffect call into a custom useClickOutside hook to avoid cluttering your component.
Let’s have a look at how our component is going so far:
So far so good! Now let’s continue with the implementation for mobile devices.
Modals seem like a very simple concept on paper: just cover the whole screen with a view on top of everything else. However, they can be quite tricky to get right especially if you want to provide an accessible modal. Therefore we’ll be leveraging Reach UI to help us manage focus states, semantic elements and other complexities associated with modal components.
Let’s start by creating our own <filtermodal ></filtermodal > component, which we can later bring into the <filter></filter> component:
-- CODE language-jsx keep-markup --
/* FilterModal.js */
import React from "react";
import { DialogContent, DialogOverlay } from "@reach/dialog";
import "./filterModal.css";
const FilterModal = React.forwardRef(
({ children, onApply, onDismiss }, ref) => {
return (
<dialogoverlay classname="filter-modal"></dialogoverlay>
ref={ref}
className="filter-modal__wrapper"
aria-label="modal window"
>
<div classname="filter-modal__header"></div>
<button onclick="{onDismiss}">x</button>
<div classname="filter-modal__content">{children}</div>
<div classname="filter-modal__actions"></div>
<button onclick="{onApply}">Apply</button>
);
}
);
export default FilterModal;
And its corresponding css:
Now we have a modal component that will only appear on small screens:
Great, we’re ready to pull this modal into our Filter component. Notice that the modal accepts a Ref. Just like with the dropdown, we’ll need to use this to prevent the modal from closing unexpectedly when the user interacts with it. We’ll also need to provide some props to handle the dismiss and apply actions:
-- CODE language-jsx keep-markup --
import React, { useState, useRef, useEffect } from "react";
import "./filter.css";
import FilterModal from "./FilterModal";
export default function Filter() {
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef(undefined);
const buttonRef = useRef(undefined);
const modalRef = useRef(undefined);
useEffect(() => {
const handleClickOutside = event => {
const isDropdownClick = dropdownRef.current && dropdownRef.current.contains(event.target);
const isButtonClick = buttonRef.current && buttonRef.current.contains(event.target);
const isModalClick = modalRef.current && modalRef.current.contains(event.target);
if (isDropdownClick || isButtonClick || isModalClick) {
/* If the ref is not defined or the user clicked on the menu, we don’t do anything. */
return;
}
/* Otherwise we close the menu. */
setIsOpen(false);
};
document.addEventListener("mousedown", handleClickOutside); /* handle desktops */
document.addEventListener("touchstart", handleClickOutside); /* handle touch devices */
/* Event cleanup */
return () => {
document.removeEventListener("mousedown", handleClickOutside); /* handle desktops */
document.removeEventListener("touchstart", handleClickOutside); /* handle touch devices */
};
}, [dropdownRef, buttonRef, modalRef]);
const handleApply = () => {
alert("Filters applied!");
setIsOpen(false);
};
return (
<div classname="filter"></div>
ref={buttonRef}
onClick={() => setIsOpen(!isOpen)}
className="filter__button"
>
Technologies
{isOpen && (
<div ref="{dropdownRef}" classname="filter__dropdown"></div>
<div style="{{" paddingtop:="" "2rem",="" paddingbottom:="" "2rem"="" }}=""></div>
{`Dropdown content goes here `}
<div classname="filter__dropdown__actions"></div>
<button onclick="{handleApply}" classname="filter__dropdown_button"></button>
Apply
)}
{isOpen && (
ref={modalRef}
onApply={handleApply}
onDismiss={() => setIsOpen(false)}
>
{`Modal content goes here `}
)}
);
}
And with that, we’ve implemented all the behavior we need for our responsive filter to handle small and large screens. But before we move on to implement features with our new component, let’s clean it up a little and make it more general purpose.
One of the best things about React is that we can extract UI blocks into reusable components. Let’s enable our Filter component to receive arbitrary content from any screen. We’ll use React’s composition model to allow our Filter to accept a children prop. This will let the parent element control what kind of content goes inside our filter component.
Let's take a look at our completed Filter component:
-- CODE language-jsx keep-markup --
import React, { useState, useRef, useEffect } from "react";
import "./filter.css";
import FilterModal from "./FilterModal";
export default function Filter({ children, onApply, label }) {
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef(undefined);
const buttonRef = useRef(undefined);
const modalRef = useRef(undefined);
useEffect(() => {
const handleClickOutside = event => {
const isDropdownClick =
dropdownRef.current && dropdownRef.current.contains(event.target);
const isButtonClick =
buttonRef.current && buttonRef.current.contains(event.target);
const isModalClick =
modalRef.current && modalRef.current.contains(event.target);
if (isDropdownClick || isButtonClick || isModalClick) {
/* If the ref is not defined or the user clicked on the menu, we don’t do anything. */
return;
}
/* Otherwise we close the menu. */
setIsOpen(false);
};
document.addEventListener("mousedown", handleClickOutside); /* handle desktops */
document.addEventListener("touchstart", handleClickOutside); /* handle touch devices */
/* Event cleanup */
return () => {
document.removeEventListener("mousedown", handleClickOutside); /* handle desktops */
document.removeEventListener("touchstart", handleClickOutside); /* handle touch devices */
};
}, [dropdownRef, buttonRef, modalRef]);
const handleApply = event => {
setIsOpen(false);
onApply(event);
};
return (
<div classname="filter"></div>
ref={buttonRef}
onClick={() => setIsOpen(!isOpen)}
className="filter__button"
>
{label}
{isOpen && (
<div ref="{dropdownRef}" classname="filter__dropdown"></div>
<div style="{{" paddingtop:="" "2rem",="" paddingbottom:="" "2rem"="" }}=""></div>
{children}
<div classname="filter__dropdown__actions"></div>
<button onclick="{handleApply}" classname="filter__dropdown_button"></button>
Apply
)}
{isOpen && (
ref={modalRef}
onApply={handleApply}
onDismiss={() => setIsOpen(false)}
>
{children}
)}
);
}
Now let's implement an actual user input to see how this all fits together. We’ll implement a multiple select filter where a user can select different technologies:
-- CODE language-jsx keep-markup --
import React, { useState } from "react";
import "./styles.css";
import Filter from "./Filter";
const technologies = ["React", "Python", "PHP", "Node.js", "Ember", "Vue"];
export default function App() {
const [selectedTechnologies, setSelectedTechnologies] = useState([]);
const handleSelect = technology => {
const isSelected = selectedTechnologies.includes(technology);
/* If the option has already been selected, we remove it from the array. */
/* Otherwise, we add it. */
const newSelection = isSelected
? selectedTechnologies.filter(currentTech => currentTech !== technology)
: [...selectedTechnologies, technology];
setSelectedTechnologies(newSelection);
};
return (
<div classname="App"></div>
<h1>Building responsive filter components on React</h1>
<filter label="Technologies" onapply="{()" ==""> alert(selectedTechnologies)}></filter>
<div classname="technologies-list"></div>
{technologies.map((tech, index) => {
const isSelected = selectedTechnologies.includes(tech);
return (
<label key="{index}"></label>
type="checkbox"
checked={isSelected}
onChange={() => {
handleSelect(tech);
}}
/>
<span classname="ml-2 text-base text-gray-500 font-heading"></span>
{tech}
);
})}
);
}
We can see how our Filter component displays a modal with the checkboxes on a mobile screen:
And how it displays a dropdown on desktop:
As a Senior Software Engineer working with other talented UI designers at FullStack Labs, a React consultancy with experience in creating complex user interfaces, I hope this article gave you a clear example of how to use composition in React to build reusable components.
This can pose a challenge when building responsive web applications that have to take into account multiple layouts and screen widths. Today, we’ll implement a filter component that adapts not only its layout but also its behavior according to the device’s screen size to provide a UI that feels comfortable on both mobile and desktop devices.
We’d love to learn more about your project.
Engagements start at $75,000.