In today’s era, we all know that most of the websites we create should be mobile-ready or mobile responsive because most of the users are on their mobile devices. And that means, if our website isn’t well responsive, we could potentially lose some users and if we lose users, we fail to attain our goal on that site.
On the other side of screen responsive for mobile, there are certain cases where on a mobile screen, you will display a different text and on the desktop screen, you will display a different text again. That sounds odd, but it can happen!
On an afternoon a few days ago, I was working on a React component named
Text that sets the standard for the values of some CSS properties like
font-size . This is how my component looks like:
It’s a simple component that accepts a prop
value that it will render on a paragraph(
p) and have some set of standard CSS values. This will allow us to be more consistent throughout our app in terms of
font-size which is great 👍 Let me now start telling you the story of my afternoon.
Let’s Get Started
My task that afternoon was to display the user’s name on the screen and the challenge is when it is on mobile we will display the first name and on desktop, we will display the user’s first name and last name.
Let’s say our user is Dan Abramov.
On mobile, we will display
On the desktop, we will display
Hi Dan Abramov!
Does that sound easy? I bet you already have a solution in mind! Here’s what we got so far.
Hide and Seek
I looked into the internet how common people solve this issue. I bet there’s a lot of solutions already out there or some libraries that will help. I found this react-responsive library that offers a solution on a React hook and through a component style! This is also the suggested library suggested by a great friend of mine.
I know there’s a lot of alternative solutions out there that we can use. A simple
css file through class names that use media queries or using a CSS-in-JS React libraries like
styled-components . But I want to try something new this time because I haven’t really done that it using that library.
So, let’s try using that now!
The above code says that when the screen reaches a width of
1224px which is the common desktop breakpoint, it will return true and false otherwise. Pretty straightforward. Cool. That solves the issue and it looks cleaner too! 🎉
But when I did observe it enough, something is missing. I remembered styled-system how it handles prop manipulation depending on the screen. If you haven’t tried styled-system, I suggest you try to! The helpers it provides are helpful! Here’s a sample code from its Typography section:
In addition to the explanation, styled-system needs a breakpoint array object that it can use as a reference behind the scenes. So, that means if I have a breakpoint of
breakpoints: ['1224px'] that means when the screen reaches the
1224px width, it will now apply the
12px font size. You can see here how it works under the hood.
But then again, styled-system only works for CSS props because it generates media queries underneath and thus it doesn’t need some overhead computations or manipulations. I wonder if it’s possible for CSS props, why not for some custom non-CSS props?
I tried looking at the internet for some solutions but I couldn’t find any or maybe I wasn’t really looking enough. If you happen to find something, let me know! When I’ve failed to find something, I began the journey to unfold something.
Curiosity Leads to Knowledge
My train of thought since the beginning was if it’s possible for the CSS props and styled-system has done it, why not for non-CSS props? I was really eager to discover how it will be done just like styled-system but for non-CSS props. The feeling of unlocking something.
So here’s the goal that I want to achieve. I want to abstract the process of detecting media queries and remove the conditioning we had before and make an API for it. This is what I’m envisioning:
My first attempt to accomplish this is to build within react-responsive since that library is capable of detecting if I am on a desktop or mobile or any device media based on a given breakpoint. It seems promising! I feel like this is it. Whew. Easy.
To get started on that, let’s create a function and I’d like to call it
pick . Our
pick function will accept a data — which is a value and a theme object that has a breakpoint property.
pick function is a higher-order function which accepts a
value and returns a function that accepts a
theme . You might wonder why did I create it as a higher-order function we can just put it in a single function argument and into an object. My reason is that I’m practicing some functional programming concepts/styles and here’s a little secret — this will allow us to skip the linting error that when we used a hook inside a function it should start with a capital or with the “use”. My
pick function uses a hook inside it but its usage is not the same with typical React hooks.
Let’s move on to the implementation:
And here’s how we will use it on our
This will allow us to achieve what we want! Here’s the whole sandbox to play around:
pick function will pick the values from the given array depending on the media query it will match. It will start from left to right reading. This is also how styled-system reads/provides the data.
But I’m not yet satisfied. I feel like hardcoding the query is less flexible and I want to dig more and see what’s more to it. The problem with that approach that’s bothering me is hardcoding the mapping of
maxWidth to my breakpoints. I want to have something like I’ll give you my breakpoints and match the query. And so I didn’t stop and continued working. Something inside me is telling me to go further. Something that is out of react-responsive.
Investigated how react-responsive works underneath, how it detects the query, how it works. What’s something I can extract there for my own goals?
I’ve discovered that there’s a native method in the
MatchMedia and to use it is like this
window.matchMedia . To learn more about it, here’s the link!
window.matchMedia accepts a media query string and returns an object containing if the given media query string matches the current screen/viewport.
And so for my second and final attempt, I removed the react-responsive and worked my way using the native match media. Built my way up using React Hooks for state management and re-rendering phases.
Given this component signature:
We will dissect how to pick the right value for our
Text component. I will show you the
pick implementation of SystemMedia. Note that, the code may change from the time of writing this.
Here are the explanations:
- Line 4 — We are getting the default value as the fallback one instead of returning empty/null. Since the user can pass an array of values or just a single string.
- Line 6 to 27 — Based on the given breakpoints, we are creating our base structure on how we will pick the values and determine the right media for our current screen/viewport.
- Line 29 — When no matching queries have been picked (i.e. mobile) we will use the default value. This is why it is important to have a default value as the fallback.
- Line 36 — We retrieved the nearest media query. We needed to retrieve the nearest one because this happens in large screens where all the media queries are matching it. Since our media query is using the
minWidth. So the idea is, get the nearest one and use it.
- Line 41 to 52 — This is the try-and-pick logic to get the value. Not very confident about this part and I’m hoping to improve this one.
Hooray! That’s our
pick function! Let’s move to the
system function where the state management and re-rendering happens. This is now the integration with React happens.
I moved the
Text component there so we all can see the usage of it. I used the
system utility function in there, pass the breakpoints and pass in the value in which it will determine what value to return as the output based on the matching media.
Here are the responsibilities of the
system utlity function that we’ve created:
- Get the initial value which happens on the first mount depending on the current screen/viewport using the
- Run the
pickfunction whenever the screen/viewport changes based on the resize event to retrieve the value.
Bringing It All Together
Sometimes the problems that we encounter gives us the opportunity to discover something new. Actually, I’ve had a lot of alternative solutions before tackling that issue and I didn’t think about using styled-system as the inspiration to create something new.
This library may not be suited to what you are working on depending on the complexity level and that’s OK. I may have said something that’s not correct and if I do, let me know and I’ll be glad to correct my mistake and learn from you as well. But I hope you can check out the Github repository and give some feedback on how we can improve it.
Thank you for joining me on this adventure. I hope you learn something new as well 🎉