Hello!
I need to detect if the request is a mobile request or not. Does Frontity have something like isMobile function? Or how can I access to the userAgent request?
Thanks!!
Hello!
I need to detect if the request is a mobile request or not. Does Frontity have something like isMobile function? Or how can I access to the userAgent request?
Thanks!!
Yes, you will be able to access any header, including âuser-agentâ, as soon as we add server extensibility. Itâs one of the next steps as we need it to make AMP work as well so it shouldnât take long.
We use Koa in the server because itâs way smaller than Express and works great with async/await. You can use this Koa functions to extend Frontityâs server at your will.
Itâs going to be very similar to this:
// packages/your-package/src/index.js
// Default export, with React app.
export default {
roots: { ... },
state: { ... },
actions: { ... },
libraries: { ... }
};
// server export, to extend the server.
export const server = app => {
app.use(ctx => {
ctx.headers['user-agent'];
});
};
If you want to include server-only code or a library that is big and donât want to include in the client bundle, youâd need to delete index.js
and create a client.js
and a server.js
file.
For example, you may want to use this: https://github.com/rvboris/koa-useragent (~15kbs):
// packages/your-package/src/client.js
export default {
state: { ... },
actions: { ... },
libraries: { ... }
};
// packages/your-package/src/server.js
import userAgent from 'koa-useragent'; // import it only in the server
export default {
state: { ... },
actions: { ... },
libraries: { ... }
};
export const server = app => {
app.use(userAgent);
app.use(ctx => {
ctx.userAgent; // do something with this
});
};
You can use the server function to modify the state
of your site. The current settings are exposed in ctx.settings
:
export const server = app => {
app.use(ctx => {
// Change the state of the settings.
ctx.settings.state.frontity.isMobile = ctx.userAgent.isMobile;
});
};
Finally, you can consume that state
in React:
const MyComponent = ({ state }) => {
if (state.frontity.isMobile) return <MobileThing />;
return <NotMobileThing />;
}
Are you going to use the user-agent
to cache different HTML for mobile/tablet/desktop?
That looks great! Until that is done on production I made a hack on @frontity/core/server/index.tsx file:
I added the
import userAgent from "koa-useragent";
After the line:
app.use(get("/favicon.ico", serve("./")));
I added:
app.use(userAgent);
And after the line:
const settings = await getSettings({ url: ctx.href, name: ctx.query.name });
Added:
settings.state.frontity.isMobile = ctx.userAgent.isMobile;
After that I ran the following commands:
sudo rm -R node_modules
npm install koa-useragent@2.0.0
npm install @types/koa-useragent
npx frontity dev
Now I can use the state.frontity.isMobile
Are you going to use the user-agent to cache different HTML for mobile/tablet/desktop?
Yes, I think I can not create the exactly the same code for mobile and desktop as there is some widgets that only needed to be loaded on mobile or desktop. If the website is behind on a CDN it must have the capability of make user-agent detection, to check if there is a mobile/tablet/desktop request. I think nowadays that is not a problem.
Thank you!
Iâm glad it works
Yes, but youâd have to make sure you are using the exact same regular expression than your CDN is using or you may get into trouble with edge cases.
They usually publish the regular expressions in the docs. For example, these are the regular expressions used by KeyCDN (Nginx syntax):
map $http_user_agent $ua_device {
default "desktop";
"~*(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge\ |maemo|midp|mmp|mobile.+firefox|netfront|opera\ m(ob|in)i|palm(\ os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows\ ce|xda|xiino/i" "mobile";
"~*android|ipad|playbook|silk/i" "tablet";
}
@javier, you know more SEO than we do but Iâd like to state a couple of things here for other people that may read this in the future:
// Hidden on desktop.
const Mobile = styled.div`
display: none;
@media (max-width: 768px) {
display: block;
}
`;
// Hidden on mobile.
const Desktop = styled.div`
display: block;
@media (max-width: 768px) {
display: none;
}
`;
const MyComponent = () => (
<>
<Mobile>
Content that only shows in mobile.
</Mobile>
<Desktop>
Content that only shows in desktop.
</Desktop>
</>
);
Of course, there are more ways to achieve that in React
Thanks for the replay! Initially I will try to avoid to have two versions. But I needed to know if it is possible do it on some way.
In fact, I have problems on the next scenario: Inside a post I have the main content with paragraphs and a sidebar with ads (ad1, ad2 and ad3) and related posts divs. I have no problem on show or hide the divs with @media query. But I canât do that with ads.
I am trying to use CSS grid (each
is a row) but is complicated calculate the height that I need for the sidebar content.
On mobile version I need to position a ad after the fourth
(for example). I canât see how can I do this only using CSS.
If you have any idea how to solve this jus tell me!
How are you positioning the ads after the fourth paragraph?
We used to do it with Html2React but I wonder whatâs your solution.
That is the problem, I donât know how to make it work using only CSS. On our actual website is generated by PHP and we have two versions (mobile and desktop). The only solution I can imagine is create ads divs duplicated and before load the smartadserver script execute a javascript logic to determine which div is visible and set it a ID attribute (wich it will be used by smartadserver script).
Have you thought about using window.matchMedia()
?
Maybe something like this:
const getType = () => {
const mq = window.matchMedia( "(max-width: 570px)" );
if (mq.matches) {
// window width is at less than 570px
return "mobile";
} else {
// window width is greater than 570px
return "desktop";
}
}
const SmartAd = ({ someAdParams, type }) => {
useEffect(() => {
// Only initialize this Ad if type matches.
if (type === getType)
initializeAd(someAdParams);
}, []);
return (
<div
id={someAdParams.tagId}
width={someAdParams.width}
height={someAdParams.height}
/>
);
};
Then use type="mobile"
or type="desktop"
in your components:
<!-- in the places where it should show an ad in mobile -->
<SmartAd someAdParams={someAdParams} type="mobile" />
<!-- in the places where it should show an ad in desktop -->
<SmartAd someAdParams={someAdParams} type="desktop" />
I wonder if you need to use the same tagId
s and that could cause problems with the initiliazation?
Hey @luisherranz, how is this going? Is it done already?
Whats the simpliest way to know if itâs mobile at the moment?
You can use beforeSSR
to get the Koa ctx
and inspect the User-Agent
header.
const beforeSSR = ({ state, actions }) => ({ ctx }) => {
const ua = ctx.get("user-agent");
// ...
}
but if you do that you cannot cache the response, so I donât recommend you to do so unless your cache layer also supports it, like this: User Device-Based Caching - KeyCDN Support.
User Agent detection is not a recommended technique for modern web apps. You can use JavaScript window.matchMedia() method to detect a mobile device based on the CSS media query.
if (window.matchMedia("(max-width: 767px)").matches)
{
// The viewport is less than 768 pixels wide
document.write("This is a mobile device.");
}
Another approach would be a responsive media query. You could presume that a mobile phone has a screen size greater than x and less than y.
For example:
@media only screen and (min-width: 320px) and (max-width: 600px) {}
You may also use navigator.userAgentData.mobile .
const isMobile = navigator.userAgentData.mobile;