Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

Sorry, you do not have permission to ask a question, You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please type your username.

Please type your E-Mail.

Please choose an appropriate title for the post.

Please choose the appropriate section so your post can be easily searched.

Please choose suitable Keywords Ex: post, video.

Browse

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Navigation

  • Home
  • About Us
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • About Us
  • Contact Us
Home/ Questions/Q 4607

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Latest Questions

Author
  • 61k
Author
Asked: November 27, 20242024-11-27T01:34:11+00:00 2024-11-27T01:34:11+00:00

How to Do a Device Based Code Split in React

  • 61k

This article is a mix of arguments, reality checks and a code solution at the end. Its focus: device (touch/desktop) driven code split in React with no backend.

Often the road leading to an actual implementation is long and bumpy – priorities, design, budget, colleagues with their own views, talking in different languages. These obstacles are challenging and usually take more energy to deal with than just coding. For that reason they deserve a separate preface here.

Jump to the code section, if this is what you are looking for, otherwise let's continue.

It would be helpful if you already know what code splitting is. If not yet, the “Code Splitting” writeup in the React docs is a good start.


Reality Check

Many companies today prefer to build their web apps/sites targeting both touch and desktop devices, but would rather not invest in a separate mobile app.

Chiefs may not admit it, but the reasons spin around:

  1. Building for the browser is fast and cheap.
  2. No need to involve the backend.
  3. Prizing “mobile first”, but don't really align with that principle.
  4. Technical impediments to deliver a mobile app to the store.
  5. No budget.

Working in the browser is fast and reliable. There are many static site generators (Gatsby, Nextjs, Docusaurus) to support website creation with no backend knowledge required. Jamstack principles and tools make production deployments of a product easier than ever. Such tools are capable of bringing the “mobile first” concept to life, though it still remains wishful thinking.

At the same time publishing a standalone mobile app to some app stores may turn into a nightmare. Read about the Hey saga fx. In contrast, javascript devs can quickly mockup a mobile version with the help of Chrome tools, so why hire an iOS/Android guy?

All valid points and to add more, often you as a frontend professional won't get the chance to influence the final decision (especially in big companies). It is to be taken by product, marketing or finance teams.

Native app or web app… Let's assume a decision is taken and you are left with no choice – a web app must be delivered (for desktop and mobile users).


If You Must Code Split

Splitting react apps touch/desktop wise can be tricky if you have to do it in the frontend.

Things to be considered:

  • 1️⃣ consider touch and desktop devices (when to serve each app)
  • 2️⃣ decide on the split starting point (where in the code)
  • 3️⃣ import only app specific components (how to implement it)

An answer to these three questions is important since maintainability, time, team motivation and other aspects very much depend on it.


When a Device Is Considered Touch 1️⃣

Usually you modify component's css to account for mobile devices.

Perhaps the following

.TopBar {   height: 60px;   background-color: #fff;   ... }  /* Mobile */ @media (max-width: 768px) {   .TopBar {     height: 100px;     background-color: #ccc;     ...   } } 
Enter fullscreen mode Exit fullscreen mode

works well for you most of the time. Same component, but with different appearance based on browser's width. There is no problem with this approach and very often it is enough. Now one may argue that max-width: 768px is sufficient to properly tell if a user is on a mobile device. Probably not. May be something like that is more accurate:

@media (pointer: coarse) and (hover: none) {   ... } 
Enter fullscreen mode Exit fullscreen mode

You can read more about interaction media features and their potential to determine device capabilities. Consider it when deciding on the criteria for serving your mobile web app.


Challenges arise when your company starts getting more serious about mobile users (“mobile first”). This could happen due to a separate strong design/UX and product teams being formed. In this reality your desktop and mobile websites/apps may end up drastically different. Business logic, pages, interactions and overall appearance are now unalike. Two independent versions of the same software.

How does that translate in the React's language?

For sure you won't be able to reuse every single component in both apps (touch and desktop). Same components/pages will require different data sets and behave non-identically (javascript logic). Others will be completely unique per app. In that case css adjustments as the one above may no longer be sufficient. Interactions and data (javascript) need to be considered along with styling (css).

This is where a proper split in the frontend must be done and it can't reside in your .css files alone.


Where to Split the App 2️⃣

It really depends. You have a few options considering requirements and design. One is to split the app in its root. Maybe you have PageRouter.js or just App.js where page components are rendered based on the URL path. Second option – split individual components. It is a good choice if pages for mobile and desktop are the same (or very similar), but some child components differ. You can also pick the third option of using media queries in the css.

Split in the App's Root

This approach makes sense if your mobile and desktop apps are very different – separate pages, behavior, data and business logic in components.

Let's say there is a product details page (<ProductDetails />) on touch which doesn't exist in your desktop site. It displays detailed product information that otherwise would be part of <Products /> when viewing on PC. On a phone, though, it might be too “noisy” to present so much data in a single page.

-- src    |-- components    |-- pages    |   |-- touch    |   |   |-- Products.js    |   |   |-- ProductDetails.js    |   |-- desktop    |   |   |-- Products.js    |   |-- common    |       |-- Checkout.js    |-- App.js 
Enter fullscreen mode Exit fullscreen mode

See a working example in Codesandbox.

Why is this structure OK?

  • More control

You can look at /touch and /desktop folders as two separate apps, allowing for full control over their content.

  • Easier maintenance

Most pages in your app will be common – same names component-wise, but implementing app specific logic, which is great for maintenance.

  • Bug fixing in isolation

Having a bug in the products page on touch tells you that the cause is probably in touch/Products.js. Fixing it there ensures your desktop page won't be affected.

  • Less side effects

Few more buttons for mobile or a dropdown on desktop? You can feel more comfortable implementing feature requests like that next time.

  • Adequate team collaboration

Implementing a products page means you have to do it for each app (two components). With the folder split above, it's easy to divide the work within the team without stepping on each other's toes.

Split on Component Level

Root level code split is often supplemented by splitting the /components folder in a similar way. On the other hand, sometimes your desktop and mobile apps won't be very different. Only a few components deep in the tree may have an unalike data model or behavior. If you find yourself in any of these cases it might be useful to do a split per component.

-- src    |-- components    |   |-- touch    |   |   |-- TopBar.js    |   |   |-- TopBar.css    |   |-- desktop    |   |   |-- TopBar.js    |   |   |-- TopBar.css    |   |-- common    |       |-- Footer.js    |       |-- Footer.css    |-- pages    |-- App.js 
Enter fullscreen mode Exit fullscreen mode

<TopBar /> component has some data/behavior differences that require you to implement it separately for each app. In the same time /common folder still contains all shared components.

You can see how that is done for /components in products page example.

Why is this structure OK?

Adding to the pros of the previous section you will have less code to maintain, since only a few components may require a split. Reusing app specific and shared components is also going to be straightforward.

import ProductDescription from "../../components/desktop/ProductDescription";  export default function Products() {   ... } 
Enter fullscreen mode Exit fullscreen mode

pages/desktop/Products imports only components from components/desktop.

Components with Styling Differences

Should you create two copies of a component if it contains the same logic, but differs in styling? Looks like it should be shared and placed in the /common folder, but at the same time its css will need the good old media query approach.

@media (max-width: 768px) { ... }  /* OR */  @media (pointer: coarse) and (hover: none) { ... } 
Enter fullscreen mode Exit fullscreen mode

That looks ok. Is it the best thing you can do, though? What if the logic detecting mobile capabilities changes? Should you change it everywhere? This is not optimal.

Ok, what to do?

Ideally the logic for detecting touch devices should be central for the app. Getting a desktop or mobile component to render should be a matter of simply tweaking a prop.

Imagine this structure:

-- src    |-- components    |   |-- touch    |   |   |-- TopBar.js    |   |   |-- TopBar.css    |   |-- desktop    |   |   |-- TopBar.js    |   |   |-- TopBar.css    |   |-- common    |       |-- TopBarLinks.js    |       |-- TopBarLinks.css    |-- pages    |-- App.js 
Enter fullscreen mode Exit fullscreen mode

<TopBarLinks /> is a shared component and may have some visual diffs. In its css this is addressed with a class.

.TopBarLinks { ... }         /* Desktop */ .TopBarLinks.touch { ... }   /* Mobile */ 
Enter fullscreen mode Exit fullscreen mode

Then it is used both in desktop/TopBar and touch/TopBar:

// desktop/TopBar.js export const TopBar = () => (   <div className="TopBar">     <img alt="Logo" src="../../assets/logo.png" />     <TopBarLinks />   </div> ); 
Enter fullscreen mode Exit fullscreen mode

and

// touch/TopBar.js export const TopBar = () => (   <div className="TopBar">     <img alt="Logo" src="../../assets/logo.png" />     <TopBarLinks touch />   </div> ); 
Enter fullscreen mode Exit fullscreen mode

That's it. This is how you can render shared components with visual diffs. As a result the css file is cleaner and independent of the device detection logic.

Enough said on the possibilities for organizing the codebase. Now, how to glue things together.


Load Components on Demand 3️⃣

No matter where the split resides in – application root or individual components, or perhaps both – its implementation is going to be the same. Ultimately the pages from all earlier examples are also components.

The task is to load only desktop OR touch related code in the browser. Loading the whole bundle (all components), but using (rendering) only device specific slices may work, but it's not optimal. A proper implementation requires you to use dynamic import().

React docs tell you that Suspense relies on that principle underneath and will probably do the job. You could also base your solution on loadable-components library. For the sake of simplicity and to cover the specific use case of touch/desktop based split, let's further focus on a plain solution.

Conditionally Import and Render Components

I personally imagine the following in the application root (App.js):

import Import from "./Import";  function App() {   return (     <div className="App">       <h1>Product page</h1>       <Import         touch={() => import("./touch/Products")}         desktop={() => import("./desktop/Products")}       >         {Product => <Product />}       </Import>     </div>   ) } 
Enter fullscreen mode Exit fullscreen mode

See it in the example Codesandbox app.

The <Import /> component (you can name it differently) accepts two props – desktop and touch. They expect a function returning a dynamic import call. In the example above there are two independent <Product /> page components that you may want to import/render conditionally.

The third prop is a children function that does the actual rendering. An obvious benefit of using render prop function here is the opportunity to explicitly pass any props to your component if needed.

{Product =>   <Product     title={product.title}     description={product.description}   /> } 
Enter fullscreen mode Exit fullscreen mode

Implementation Details

What will Import do internally is to: evaluate which component to load and pass it down as an argument to the render prop function.

Basic implementation may look like:

// Detect touch enabled devices based on interaction media features // Not supported in IE11, in which case isMobile will be 'false' const isMobile =   window.matchMedia("(pointer: coarse) and (hover: none)").matches;  export function Import({ touch, desktop, children }) {   const [Component, setComponent] = useState(null);    useEffect(() => {     // Assign a callback with an import() call     const importCallback = isMobile ? touch : desktop;      // Executes the 'import()' call that returns a promise with     // component details passed as an argument     importCallback().then(componentDetails => {       // Set the import data in the local state       setComponent(componentDetails);     });   }, [desktop, touch]);    // The actual component is assigned to the 'default' prop   return children(Component ? Component.default : () => null); } 
Enter fullscreen mode Exit fullscreen mode

More on Import and its usage – check the app context.

Some notes:

  1. window.matchMedia("(pointer: coarse) and (hover: none)") – you can use any other mechanism for detecting touch capabilities here. Going a step further, isMobile may come from the store instead (if you are using redux, mobx or other global state management mechanism).

  2. importCallback().then(componentDetails) – the actual component is set in componentDetails.default and you have to export it using default export (export default function Products()).

  3. Finally, imported data is set to the local state and your component passed down to the children function for rendering.

Using import() requires some prerequisites to allow for proper parsing and dividing the final bundle in parts. You may need to additionally set these up.

Webpack Config

For the split to work there are some adjustments in the webpack config file to be made. An example config by Dan Abramov can be found on github. If you are using Create React App that is done by default.

module.exports = {   entry: {     main: './src/App.js',   },   output: {     filename: "bundle.js",     chunkFilename: "chunk.[id].js",     path: './dist',     publicPath: 'dist/'   } }; 
Enter fullscreen mode Exit fullscreen mode

Babel Plugin

If you are using Babel the @babel/plugin-syntax-dynamic-import plugin is required in order to properly parse dynamic imports.

Eslint Config

eslint-plugin-import is also required to support export/import syntax. Don't forget to update your eslint config file:

{   parser: "babel-eslint",   plugins: ["import"]   ... } 
Enter fullscreen mode Exit fullscreen mode

Again code splitting is supported by default with Create React App and you can skip the config steps in that case.


Final Words

Check the full code implementation in Codesandbox for details on device based code splitting.

I would like to wrap up by sharing my own motivation for having app structure like the one described. It may not be your case, but my observations show a common mindset especially in big corps where a clear separation between product, backend and frontend is in place.

In that reality it's much easier (and often the only thing you can do) to overcome process issues with a tech solution, instead of trying to change people.

Here is an example: you know that backend will deliver the API in a week, but you also know that you can deliver the UI today. Waiting one week for the backend? The slow backend delivery might be due to organizational issues. The tech solution in that case is to mock the payload and deliver to QA and Product teams early.

The same motive plays its role when trying to avoid the backend by carefully code splitting the app.

Frontend-only app split will allow for:

  • development speed as per less backend deps
  • flexibility when changes are requested

It also means less headache by not having to confront colleagues and management, and higher confidence as you remain in the javascript land – your comfortable area of expertise.

📩

If you face process or code challenges Google Search can't help you with, join my readers group. I send monthly updates with posts like this.


Resources

  • Code-Splitting – React – from React's official docs.
  • Code Splitting – Create React App – words on code splitting in create-react-app.
  • Suspense for Data Fetching – get an overview on how and when to use React Suspense.
  • Webpack Code Splitting – configure webpack to support code split in your React app.
  • Loadable Components – Guide – Getting started guide for loadable-components.
  • Interaction Media Features and Their Potential – how to better determine device capabilities with media queries.

javascriptmobilereactwebdev
  • 0 0 Answers
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

Sidebar

Ask A Question

Stats

  • Questions 4k
  • Answers 0
  • Best Answers 0
  • Users 1k
  • Popular
  • Answers
  • Author

    How to ensure that all the routes on my Symfony ...

    • 0 Answers
  • Author

    Insights into Forms in Flask

    • 0 Answers
  • Author

    Kick Start Your Next Project With Holo Theme

    • 0 Answers

Top Members

Samantha Carter

Samantha Carter

  • 0 Questions
  • 20 Points
Begginer
Ella Lewis

Ella Lewis

  • 0 Questions
  • 20 Points
Begginer
Isaac Anderson

Isaac Anderson

  • 0 Questions
  • 20 Points
Begginer

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help

Footer

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore, ask, and connect. Join our vibrant Q&A community today!

About Us

  • About Us
  • Contact Us
  • All Users

Legal Stuff

  • Terms of Use
  • Privacy Policy
  • Cookie Policy

Help

  • Knowledge Base
  • Support

Follow

© 2022 Querify Question. All Rights Reserved

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.