Tag: reactjs

  • Mastering TV App Development: Building Seamless Experiences with EnactJS and WebOS

    As the world of smart TVs evolves, delivering immersive and seamless viewing experiences is more crucial than ever. At Velotio Technologies, we take pride in our proven expertise in crafting high-quality TV applications that redefine user engagement. Over the years, we have built multiple TV apps across diverse platforms, and our mastery of cutting-edge JavaScript frameworks, like EnactJS, has consistently set us apart.

    Our experience extends to WebOS Open Source Edition (OSE), a versatile and innovative platform for smart device development. WebOS OSE’s seamless integration with EnactJS allows us to deliver native-quality apps optimized for smart TVs that offer advanced features like D-pad navigation, real-time communication with system APIs, and modular UI components.

    This blog delves into how we harness the power of WebOS OSE and EnactJS to build scalable, performant TV apps. Learn how Velotio’s expertise in JavaScript frameworks and WebOS technologies drive innovation, creating seamless, future-ready solutions for smart TVs and beyond.

    This blog begins by showcasing the unique features and capabilities of WebOS OSE and EnactJS. We then dive into the technical details of my development journey — building a TV app with a web-based UI that communicates with proprietary C++ modules. From designing the app’s architecture to overcoming platform-specific challenges, this guide is a practical resource for developers venturing into WebOS app development.

    What Makes WebOS OSE and EnactJS Stand Out?

    • Native-quality apps with web technologies: Develop lightweight, responsive apps using familiar HTML, CSS, and JavaScript.
    • Optimized for TV and beyond: EnactJS offers seamless D-pad navigation and localization for Smart TVs, along with modularity for diverse platforms like automotive and IoT.
    • Real-time integration with system APIs: Use Luna Bus to enable bidirectional communication between the UI and native services.
    • Scalability and customization: Component-based architecture allows easy scaling and adaptation of designs for different use cases.
    • Open source innovation: WebOS OSE provides an open, adaptable platform for developing cutting-edge applications.

    What Does This Guide Cover?

    The rest of this blog details my development experience, offering insights into the architecture, tools, and strategies for building TV apps:

    • R&D and Designing the Architecture
    • Choosing EnactJS for UI Development
    • Customizing UI Components for Flexibility
    • Navigation Strategy for TV Apps
    • Handling Emulation and Simulation Gaps
    • Setting Up the Development Machine for the Simulator
    • Setting Up the Development Machine for the Emulator
    • Real-Time Updates (Subscription) with Luna Bus Integration
    • Packaging, Deployment, and App Updates

    R&D and Designing the Architecture

    The app had to connect a web-based interface (HTML, CSS, JS) to proprietary C++ services interacting with system-level processes. This setup is uncommon for WebOS OSE apps, posing two core challenges:

    1. Limited documentation: Resources for WebOS app development were scarce.
    2. WebAssembly infeasibility: Converting the C++ module to WebAssembly would restrict access to system-level processes.

    Solution: An Intermediate C++ Service capable of interacting with both the UI and other C++ modules

    To bridge these gaps, I implemented an intermediate C++ service to:

    • Communicate between the UI and the proprietary C++ service.
    • Use Luna Bus APIs to send and receive messages.

    This approach not only solved the integration challenges but also laid a scalable foundation for future app functionality.

    Architecture

    The WebApp architecture employs MVVM (Model-View-ViewModel), Component-Based Architecture (CBA), and Atomic Design principles to achieve modularity, reusability, and maintainability.

    App Architecture Highlights:

    • WebApp frontend: Web-based UI using EnactJS.
    • External native service: Intermediate C++ service (w/ Client SDK) interacting with the UI via Luna Bus.
    Block Diagram of the App Architecture

    ‍Choosing EnactJS for UI Development

    With the integration architecture in place, I focused on UI development. The D-pad compatibility required for smart TVs narrowed the choice of frameworks to EnactJS, a React-based framework optimized for WebOS apps.

    Why EnactJS?

    • Built-in TV compatibility: Supports remote navigation out-of-the-box.
    • React-based syntax: Familiar for front-end developers.

    Customizing UI Components for Flexibility

    EnactJS’s default components had restrictive customization options and lacked the flexibility for the desired app design.

    Solution: A Custom Design Library

    I reverse-engineered EnactJS’s building blocks (e.g., Buttons, Toggles, Popovers) and created my own atomic components aligned with the app’s design.

    This approach helped in two key ways:

    1. Scalability: The design system allowed me to build complex screens using predefined components quickly.
    2. Flexibility: Complete control over styling and functionality.

    Navigation Strategy for TV Apps

    In the absence of any recommended navigation tool for WebOS, I employed a straightforward navigation model using conditional-based routing:

    1. High-level flow selection: Determining the current process (e.g., Home, Settings).
    2. Step navigation: Tracking the user’s current step within the selected flow.

    This conditional-based routing minimized complexity and avoided adding unnecessary tools like react-router.

    Handling Emulation and Simulation Gaps

    The WebOS OSE simulator was straightforward to use and compatible with Mac and Linux. However, testing the native C++ services needed a Linux-based emulator.

    The Problem: Slow Build Times Cause Slow Development

    Building and deploying code on the emulator had long cycles, drastically slowing development.

    Solution: Mock Services

    To mitigate this, I built a JavaScript-based mock service to replicate the native C++ functionality:

    • On Mac, I used the mock service for rapid UI iterations on the Simulator.
    • On Linux, I swapped the mock service with the real native service for final testing on the Emulator.

    This separation of development and testing environments streamlined the process, saving hours during the UI and flow development.

    Setting Up the Development Machine for the Simulator

    To set up your machine for WebApp development with a simulator, ensure you install the VSCode extensions — webOS Studio, Git, Python3, NVM, and Node.js.

    Install WebOS OSE CLI (ares) and configure the TV profile using ares-config. Then, clone the repository, install the dependencies, and run the WebApp in watch mode with npm run watch.

    Install the “webOS Studio” extension in VSCode and set up the WebOS TV 24 Simulator via the Package Manager or manually. Finally, deploy and test the app on the simulator using the extension and inspect logs directly from the virtual remote interface.

    Note: Ensure the profile is set to TV because the simulator only works only for the TV profile.

    ares-config --profile tv

    Setting Up the Development Machine for the Emulator

    To set up your development machine for WebApp and Native Service development with an emulator, ensure you have a Linux machine and WebOS OSE CLI.

    Install essential tools like Git, GCC, Make, CMake, Python3, NVM, and VirtualBox.

    Build the WebOS Native Development Kit (NDK) using the build-webos repository, which may take 8–10 hours.

    Configure the emulator in VirtualBox and add it as a target device using the ares-setup-device. Clone the repositories, build the WebApp and Native Service, package them into an IPK, install it on the emulator using ares-install, and launch the app with ares-launch.

    Setting Up the Target Device for Ares Command to be Able to Identify the Emulator

    This step is required before you can install the IPK to the emulator.

    Note: To find the IP address of the WebOS Emulator, go to Settings -> Network -> Wired Connection.

    ares-setup-device --add target -i "host=192.168.1.1" -i "port=22" -i "username=root" -i "default=true"

    Real-Time Updates (Subscription) with Luna Bus Integration

    One feature required real-time updates from the C++ module to the UI. While the Luna Bus API provided a means to establish a subscription, I encountered challenges with:

    • Lifecycle Management: Re-subscriptions would fail due to improper cleanup.

    Solution: Custom Subscription Management

    I designed a custom logic layer for stable subscription management, ensuring seamless, real-time updates without interruptions.

    Packaging, Deployment, and App Updates

    Packaging

    Pack a dist of the Enact app, make the native service, and then use the ares-package command to build an IPK containing both the dist and the native service builds.

    npm run pack
    
    cd com.example.app.controller
    mkdir BUILD
    cd BUILD
    source /usr/local/webos-sdk-x86_64/environment-setup-core2-64-webos-linux
    cmake ..
    make
    
    ares-package -n app/dist webos/com.example.app.controller/pkg_x86_64

    Deployment

    The external native service will need to be packaged with the UI code to get an IPK, which can then be installed on the WebOS platform manually.

    ares-install com.example.app_1.0.0_all.ipk -d target
    ares-launch com.example.app -d target

    App Updates

    The app updates need to be sent as Firmware-Over-the-Air (FOTA) — based on libostree.

    WebOS OSE 2.0.0+ supports Firmware-Over-the-Air (FOTA) using libostree, a “git-like” system for managing Linux filesystem upgrades. It enables atomic version upgrades without reflashing by storing sysroots and tracking filesystem changes efficiently. The setup involves preparing a remote repository on a build machine, configuring webos-local.conf, and building a webos-image. Devices upgrade via commands to fetch and deploy rootfs revisions. Writable filesystem support (hotfix mode) allows temporary or persistent changes. Rollback requires manually reconfiguring boot deployment settings. Supported only on physical devices like Raspberry Pi 4, not emulators, FOTA simplifies platform updates while conserving disk space.

    Key Learnings and Recommendations

    1. Mock Early, Test Real: Use mock services for UI development and switch to real services only during final integration.
    2. Build for Reusability: Custom components and a modular architecture saved time during iteration.
    3. Plan for Roadblocks: Niche platforms like WebOS require self-reliance and patience due to limited community support.

    Conclusion: Mastering WebOS Development — A Journey of Innovation

    Building a WebOS TV app was a rewarding challenge. With WebOS OSE and EnactJS, developers can create native-quality apps using familiar web technologies. WebOS OSE stands out for its high performance, seamless integration, and robust localization support, making it ideal for TV app development and beyond (automotive, IOT, and robotics). Pairing it with EnactJS, a React-based framework, simplifies the process with D-pad compatibility and optimized navigation for TV experiences.

    This project showed just how powerful WebOS and EnactJS can be in building apps that bridge web-based UIs and C++ backend services. Leveraging tools like Luna Bus for real-time updates, creating a custom design system, and extending EnactJS’s flexibility allowed for a smooth and scalable development process.

    The biggest takeaway is that developing for niche platforms like WebOS requires persistence, creativity, and the right approach. When you face roadblocks and there’s limited help available, try to come up with your own creative solutions, and persist! Keep iterating, learning, and embracing the journey, and you’ll be able to unlock exciting possibilities.

  • Why Signals Could Be the Future for Modern Web Frameworks?

    Introduction

    When React got introduced, it had an edge over other libraries and frameworks present in that era because of a very interesting concept called one-way data binding or in simpler words uni-directional flow of data introduced as a part of Virtual DOM.

    It made for a fantastic developer experience where one didn’t have to think about how the updates flow in the UI when data (”state” to be more technical) changes.

    However, as more and more hooks got introduced there were some syntactical rules to make sure they perform in the most optimum way. Essentially, a deviation from the original purpose of React which is unidirectional flow or explicit mutations

    To call out a few

    • Filling out the dependency arrays correctly
    • Memoizing the right values or callbacks for rendering optimization
    • Consciously avoiding prop drilling

    And possibly a few more that if done the wrong way could cause some serious performance issues i.e. everything just re-renders. A slight deviation from the original purpose of just writing components to build UIs.

    The use of signals is a good example of how adopting Reactive programming primitives can help remove all this complexity and help improve developer experience by shifting focus on the right things without having to explicitly follow a set of syntactical rules for gaining performance.

    What Is a Signal?

    A signal is one of the key primitives of Reactive programming. Syntactically, they are very similar to states in React. However, the reactive capabilities of a signal is what gives it the edge.

    const [state, setState] = useState(0);
    // state -> value
    // setState -> setter
    const [signal, setSignal] = createSignal(0);
    // signal -> getter 
    // setSignal -> setter

    At this point, they look pretty much the same—except that useState returns a value and useSignal returns a getter function.

    How is a signal better than a state?

    Once useState returns a value, the library generally doesn’t concern itself with how the value is used. It’s the developer who has to decide where to use that value and has to explicitly make sure that any effects, memos or callbacks that want to subscribe to changes to that value has that value mentioned in their dependency list and in addition to that, memoizing that value to avoid unnecessary re-renders. A lot of additional effort.

    function ParentComponent() {
      const [state, setState] = useState(0);
      const stateVal = useMemo(() => {
        return doSomeExpensiveStateCalculation(state);
      }, [state]); // Explicitly memoize and make sure dependencies are accurate
      
      useEffect(() => {
        sendDataToServer(state);
      }, [state]); // Explicilty call out subscription to state
      
      return (
        <div>
          <ChildComponent stateVal={stateVal} />
        </div>
      );
    }

    A createSignal, however, returns a getter function since signals are reactive in nature. To break it down further, signals keep track of who is interested in the state’s changes, and if the changes occur, it notifies these subscribers.

    To gain this subscriber information, signals keep track of the context in which these state getters, which are essentially a function, are called. Invoking the getter creates a subscription.

    This is super helpful as the library is now, by itself, taking care of the subscribers who are subscribing to the state’s changes and notifying them without the developer having to explicitly call it out.

    createEffect(() => {
      updateDataElswhere(state());
    }); // effect only runs when `state` changes - an automatic subscription

    The contexts (not to be confused with React Context API) that are invoking the getter are the only ones the library will notify, which means memoizing, explicitly filling out large dependency arrays, and the fixing of unnecessary re-renders can all be avoided. This helps to avoid using a lot of additional hooks meant for this purpose, such as useRef, useCallback, useMemo, and a lot of re-renders.

    This greatly enhances the developer experience and shifts focus back on building components for the UI rather than spending that extra 10% of developer efforts in abiding by strict syntactical rules for performance optimization.

    function ParentComponent() {
      const [state, setState] = createSignal(0);
      const stateVal = doSomeExpensiveStateCalculation(state()); // no need memoize explicity
    
      createEffect(() => {
        sendDataToServer(state());
      }); // will only be fired if state changes - the effect is automatically added as a subscriber
    
      return (
        <div>
          <ChildComponent stateVal={stateVal} />
        </div>
      );
    }

    Conclusion

    It might look like there’s a very biased stance toward using signals and reactive programming in general. However, that’s not the case.

    React is a high-performance, optimized library—even though there are some gaps or misses in using your state in an optimum way, which leads to unnecessary re-renders, it’s still really fast. After years of using React a certain way, frontend developers are used to visualizing a certain flow of data and re-rendering, and replacing that entirely with a reactive programming mindset is not natural. React is still the de facto choice for building user interfaces, and it will continue to be with every iteration and new feature added.

    Reactive programming, in addition to performance enhancements, also makes the developer experience much simpler by boiling down to three major primitives: Signal, Memo, and Effects. This helps focus more on building components for UIs rather than worrying about dealing explicitly with performance optimization.

    Signals are increasingly getting popular and are a part of many modern web frameworks, such as Solid.js, Preact, Qwik, and Vue.js.

  • Demystifying UI Frameworks and Theming for React Apps

    Introduction:

    In this blog, we will be talking about design systems, diving into the different types of CSS frameworks/libraries, then looking into issues that come with choosing a framework that is not right for your type of project. Then we will be going over different business use cases where these different frameworks/libraries match their requirements.

    Let’s paint a scenario: when starting a project, you start by choosing a JS framework. Let’s say, for example, that you went with a popular framework like React. Depending on whether you want an isomorphic app, you will look at Next.js. Next, we choose a UI framework, and that’s when our next set of problems appears.

    WHICH ONE?

    It’s hard to go with even the popular ones because it might not be what you are looking for. There are different types of libraries handling different types of use cases, and there are so many similar ones that each handle stuff slightly differently.

    These frameworks come and go, so it’s essential to understand the fundamentals of CSS. These libraries and frameworks help you build faster; they don’t change how CSS works.

    But, continuing with our scenario, let’s say we choose a popular library like Bootstrap, or Material. Then, later on, as you’re working through the project, you notice issues like:

    – Overriding default classes more than required 

    – End up with ugly-looking code that’s hard to read

    – Bloated CSS that reduces performance (flash of unstyled content issues, reduced CLS, FCP score)

    – Swift changing designs, but you’re stuck with rigid frameworks, so migrating is hard and requires a lot more effort

    – Require swift development but end up building from scratch

    – Ending up with a div soup with no semantic meaning

    To solve these problems and understand how these frameworks work, we have segregated them into the following category types. 

    We will dig into each category and look at how they work, their pros/cons and their business use case.

    Categorizing the available frameworks:

    Vanilla Libraries 

    These libraries allow you to write vanilla CSS with some added benefits like vendor prefixing, component-level scoping, etc.  You can use this as a building block to create your own styling methodology. Essentially, it’s mainly CSS in JS-type libraries that come in this type of category. CSS modules would also come under these as well since you are writing CSS in a module file.

    Also, inline styles in React seem to resemble a css-in-js type method, but they are different. For inline styles, you would lose out on media queries, keyframe animations, and selectors like pseudo-class, pseudo-element, and attribute selectors. But css-in-js type libraries have these abilities.  

    It also differs in how the out the CSS; inline styling would result in inline CSS in the HTML for that element, whereas css-in-js outputs as internal styles with class names.

    Nowadays, these css-in-js types are popular for their optimized critical render path strategy for performance.

    Example:

    Emotion

    import styled from @emotion/styled';
    const Button = styled.button`
        padding: 32px;
        background-color: hotpink;
        font-size: 24px;
        border-radius: 4px;
        color: black;
        font-weight: bold;
        &:hover {
            color: white;
        }
    `
    render(<Button>This my button component.</Button>)

    Styled Components

    const Button = styled.a`
    /* This renders the buttons above... Edit me! */
    display: inline-block;
    border-radius: 3px;
    padding: 0.5rem 0;
    margin: 0.5rem 1rem;
    width: 11rem;
    background: transparent;
    color: white;
    border: 2px solid white;
    /* The GitHub button is a primary button
    * edit this to target it specifically! */
    ${propsprops. primary && css`
    background: white;
    color: black;`} 

    List of example frameworks: 

       – Styled components

       – Emotion

       – Vanilla-extract

       – Stitches

       – CSS modules
    (CSS modules is not an official spec or an implementation in the browser, but rather, it’s a process in a build step (with the help of Webpack or Browserify) that changes class names and selectors to be scoped.)

    Pros:

    • Fully customizable—you can build on top of it
    • Doesn’t bloat CSS, only loads needed CSS
    • Performance
    • Little to no style collision

    Cons:

    • Requires effort and time to make components from scratch
    • Danger of writing smelly code
    • Have to handle accessibility on your own

    Where would you use these?

    • A website with an unconventional design that must be built from scratch.
    • Where performance and high webvital scores are required—the performance, in this case, refers to an optimized critical render path strategy that affects FCP and CLS.
    • Generally, it would be user-facing applications like B2C.

    Unstyled / Functional Libraries

    Before coming to the library, we would like to cover a bit on accessibility.

    Apart from a website’s visual stuff, there is also a functional aspect, accessibility.

    And many times, when we say accessibility in the context of web development, people automatically think of screen readers. But it doesn’t just mean website accessibility to people with a disability; it also means enabling as many people as possible to use your websites, even people with or without disabilities or people who are limited. 

    Different age groups

    Font size settings on phones and browser settings should be reflected on the app

    Situational limitation

    Dark mode and light mode

    Different devices

    Mobile, desktop, tablet

    Different screen sizes

    Ultra wide 21:9, normal monitor screen size 16:9 

    Interaction method

    Websites can be accessible with keyboard only, mouse, touch, etc.

    But these libraries mostly handle accessibility for the disabled, then interaction methods and focus management. The rest is left to developers, which includes settings that are more visual in nature, like screen sizes, light/dark mode etc.

    In general, ARIA attributes and roles are used to provide information about the interaction of a complex widget. The libraries here sprinkle this information onto their components before giving them to be styled.

    So, in short, these are low-level UI libraries that handle the functional part of the UI elements, like accessibility, keyboard navigation, or how they work. They come with little-to-no styling, which is meant to be overridden.

    Radix UI

    // Compose a Dialog with custom focus management
    export const InfoDialog = ({ children }) => {
        const dialogCloseButton = React.useRef(null);
        return (
            <Dialog.Root>
                <Dialog.Trigger>View details</Dialog.Trigger>
                <Dialog.Overlay />
                <Dialog.Portal>
                    <DialogContent
                        onOpenAutoFocus={(event) => {
                        // Focus the close button when dialog opens
                            dialogCloseButton.current?.focus();
                            event.preventDefault();
                        }}>
                        {children}
                        <Dialog.Close ref={dialogCloseButton}>
                            Close
                        </Dialog.Close>
                    </DialogContent>
                </Dialog.Portal>
            </Dialog.Root>
        )
    } 

    React Aria

    import React from "react";
    function Breadcrumbs (props) {
        let { navProps } = useBreadcrumbs(props);
        let children = React. Children.toArray (props.children);
        return (
            <nav {...navProps}>
                <ol style={{ display: 'flex', listStyle: 'none', margin: 0}}>
                    {children.map((child, i) => React.cloneElement(child, { isCurrent: i === children.le})
                    )}
                </ol>
            </nav>
        )
    }

    List of the frameworks:

    • Radix UI
    • Reach UI
    • React Aria, React Stately (by Adobe)
    • Headless-UI

    Pros:

    • Gives perfect accessibility and functionality
    • Gives the flexibility to create composable elements
    • Unopinionated styling, free to override

    Cons:

    • Can’t be used for a rapid development project or prototyping
    • Have to understand the docs thoroughly to continue development at a normal pace

    Where would you use these?

    • Websites like news or articles won’t require this.
    • Applications where accessibility is more important than styling and design (Government websites, banking, or even internal company apps).
    • Applications where importance is given to both accessibility and design, so customizability to these components is preferred (Teamflow, CodeSandbox, Vercel).
    • Can be paired with Vanilla libraries to provide performance with accessibility.
    • Can be paired with utility-style libraries to provide relatively faster development with accessibility.

    Utility Styled Library / Framework

    These types of libraries allow you to style your elements through their interfaces, either through class names or component props using composable individual CSS properties as per your requirements. The strongest point you have with such libraries is the flexibility of writing custom CSS properties. With these libraries, you would often require a “wrapper” class or components to be able to reuse them. 

    These libraries dump these utility classes into your HTML, impacting your performance. Though there is still an option to improve the performance by purging the unused CSS from your project in a build step, even with that, the performance won’t be as good as css-in-js. The purging would look at the class names throughout the whole project and remove them if there is no reference. So, when loading a page, it would still load CSS that is not being used on the current page but another one.

    Tailwind

    const people = [
      {
        name: 'Calvin Hawkins',
        email: 'calvin.hawkins@example.com',
        image:
          'https://images.unsplash.com/photo-1491528323818-fdd1faba62cc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80',
      },
      {
        name: 'Kristen Ramos',
        email: 'kristen.ramos@example.com',
        image:
          'https://images.unsplash.com/photo-1550525811-e5869dd03032?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80',
      },
      {
        name: 'Ted Fox',
        email: 'ted.fox@example.com',
        image:
          'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80',
      },
    ]
    
    export default function Example() {
      return (
        <ul className="divide-y divide-gray-200">
          {people.map((person) => (
            <li key={person.email} className="py-4 flex">
              <img className="h-10 w-10 rounded-full" src={person.image} alt="" />
              <div className="ml-3">
                <p className="text-sm font-medium text-gray-900">{person.name}</p>
                <p className="text-sm text-gray-500">{person.email}</p>
              </div>
            </li>
          ))}
        </ul>
      )
    }

    Chakra UI

    import { MdStar } from "react-icons/md";
    
    export default function Example() {
      return (
        <Center h="100vh">
          <Box p="5" maxW="320px" borderWidth="1px">
            <Image borderRadius="md" src="https://bit.ly/2k1H1t6" />
            <Flex align="baseline" mt={2}>
              <Badge colorScheme="pink">Plus</Badge>
              <Text
                ml={2}
                textTransform="uppercase"
                fontSize="sm"
                fontWeight="bold"
                color="pink.800"
              >
                Verified • Cape Town
              </Text>
            </Flex>
            <Text mt={2} fontSize="xl" fontWeight="semibold" lineHeight="short">
              Modern, Chic Penthouse with Mountain, City & Sea Views
            </Text>
            <Text mt={2}>$119/night</Text>
            <Flex mt={2} align="center">
              <Box as={MdStar} color="orange.400" />
              <Text ml={1} fontSize="sm">
                <b>4.84</b> (190)
              </Text>
            </Flex>
          </Box>
        </Center>
      );
    }

    List of the frameworks

    • Tailwind
    • Chakra UI (although it has some prebuilt components, its concept is driven from Tailwind)
    • Tachyons
    • xStyled

    Pros:

    • Rapid development and prototyping
    • Gives flexibility to styling
    • Enforces a little consistency; you don’t have to use magic numbers while creating the layout (spacing values, responsive variables like xs, sm, etc.)
    • Less context switching—you’ll write CSS in your HTML elements

    Cons:

    • Endup with ugly-looking/hard-to-read code
    • Lack of importance to components, you would have to handle accessibility yourself
    • Creates a global stylesheet that would have unused classes

    Where would you use these?

    • Easier composition of simpler components to build large applications.
    • Modular applications where rapid customization is required, like font sizes, color pallets, themes, etc.
    • FinTech or healthcare applications where you need features like theme-based toggling in light/dark mode to be already present.
    • Application where responsive design is supported out of the box, along with ease of accessibility and custom breakpoints for responsiveness.

    Pre-styled / All-In-One Framework 

    These are popular frameworks that come with pre-styled, ready-to-use components out of the box with little customization.

    These are heavy libraries that have fixed styling that can be overridden. However, generally speaking, overriding the classes would just load in extra CSS, which just clogs up the performance. These kinds of libraries are generally more useful for rapid prototyping and not in places with heavy customization and priority on performance.

    These are quite beginner friendly as well, but if you are a beginner, it is best to understand the basics and fundamentals of CSS rather than fully relying on frameworks like these as your crutches. Although, these frameworks have their pros with speed of development.

    Material UI

    <Box
            component="form"
            className="lgn-form-content"
            id="loginForm"
            onSubmit={formik.handleSubmit}
          >
            <Input
              id="activationCode"
              placeholder="Enter 6 Digit Auth Code"
              className="lgn-form-input"
              type="text"
              onChange={formik.handleChange}
              value={formik.values.activationCode}
            />
    
            <Button
              sx={{ marginBottom: "24px", marginTop: "1rem" }}
              type="submit"
              className="lgn-form-submit"
              form="loginForm"
              onKeyUp={(e) =>
                keyUpHandler(e, formik.handleSubmit, formik.isSubmitting)
              }
            >
              <Typography className="lgn-form-submit-text">
                Activate & Sign In
              </Typography>
            </Button>
            {formik.errors.activationCode && formik.touched.activationCode ? (
              <Typography color="white">{formik.errors.activationCode}</Typography>
            ) : null}
    </Box>

    BootStrap

    <Accordion isExpanded={true} useArrow={true}>
       <AccordionLabel className="editor-accordion-label">RULES</AccordionLabel>
       <AccordionSection>
         <div className="editor-detail-panel editor-detail-panel-column">
           <div className="label">Define conditional by adding a rule</div>
           <div className="rule-actions"></div>
         </div>
       </AccordionSection>
    </Accordion>

    List of the framework:

    • Bootstrap
    • Semantic UI
    • Material UI
    • Bulma
    • Mantine 

    Pros: 

    • Faster development, saves time since everything comes out of the box.
    • Helps avoid cross-browser bugs
    • Helps follow best practices (accessibility)

    Cons:

    • Low customization
    • Have to become familiar with the framework and its nuisance
    • Bloated CSS, since its loading in everything from the framework on top of overridden styles

    Where would you use these?

    • Focus is not on nitty-gritty design but on development speed and functionality.
    • Enterprise apps where the UI structure of the application isn’t dynamic and doesn’t get altered a lot.
    • B2B apps mostly where the focus is on getting the functionality out fast—UX is mostly driven by ease of use of the functionality with a consistent UI design.
    • Applications where you want to focus more on cross-browser compatibility.

    Conclusion:

    This is not a hard and fast rule; there are still a bunch of parameters that aren’t covered in this blog, like developer preference, or legacy code that already uses a pre-existing framework. So, pick one that seems right for you, considering the parameters in and outside this blog and your judgment.

    To summarize a little on the pros and cons of the above categories, here is a TLDR diagram:

    Pictorial Representation of the Summary

  • Scalable Real-time Communication With Pusher

    What and why?

    Pusher is a hosted API service which makes adding real-time data and functionality to web and mobile applications seamless. 

    Pusher works as a real-time communication layer between the server and the client. It maintains persistent connections at the client using WebSockets, as and when new data is added to your server. If a server wants to push new data to clients, they can do it instantly using Pusher. It is highly flexible, scalable, and easy to integrate. Pusher has exposed over 40+ SDKs that support almost all tech stacks.

    In the context of delivering real-time data, there are other hosted and self-hosted services available. It depends on the use case of what exactly one needs, like if you need to broadcast data across all the users or something more complex having specific target groups. In our use case, Pusher was well-suited, as the decision was based on the easy usage, scalability, private and public channels, webhooks, and event-based automation. Other options which we considered were Socket.IO, Firebase & Ably, etc. 

    Pusher is categorically well-suited for communication and collaboration features using WebSockets. The key difference with  Pusher: it’s a hosted service/API.  It takes less work to get started, compared to others, where you need to manage the deployment yourself. Once we do the setup, it comes to scaling, that reduces future efforts/work.

    Some of the most common use cases of Pusher are:

    1. Notification: Pusher can inform users if there is any relevant change.  Notifications can also be thought of as a form of signaling, where there is no representation of the notification in the UI. Still, it triggers a reaction within an application.

    2. Activity streams: Stream of activities which are published when something changes on the server or someone publishes it across all channels.

    3. Live Data Visualizations: Pusher allows you to broadcast continuously changing data when needed.

    4. Chats: You can use Pusher for peer to peer or peer to multichannel communication.

    In this blog, we will be focusing on using Channels, which is an alias for Pub/Sub messaging API for a JavaScript-based application. Pusher also comes with Chatkit and Beams (Push Notification) SDK/APIs.

    • Chatkit is designed to make chat integration to your app as simple as possible. It allows you to add group chat and 1 to 1 chat feature to your app. It also allows you to add file attachments and online indicators.
    • Beams are used for adding Push Notification in your Mobile App. It includes SDKs to seamlessly manage push token and send notifications.

    Step 1: Getting Started

    Setup your account on the Pusher dashboard and get your free API keys.

    Image Source: Pusher

    1. Click on Channels
    2. Create an App. Add details based on the project and the environment
    3. Click on the App Keys tab to get the app keys.
    4. You can also check the getting started page. It will give code snippets to get you started.

    Add Pusher to your project:

    var express = require('express');
    var bodyParser = require('body-parser');
    
    var app = express();
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: false }));
    
    app.post('/pusher/auth', function(req, res) {
      var socketId = req.body.socket_id;
      var channel = req.body.channel_name;
      var auth = pusher.authenticate(socketId, channel);
      res.send(auth);
    });
    
    var port = process.env.PORT || 5000;
    app.listen(port);

    CODE: https://gist.github.com/velotiotech/f09f14363bacd51446d5318e5050d628.js

    or using npm

    npm i pusher

    CODE: https://gist.github.com/velotiotech/423115d0943c1b882c913e437c529d11.js

    Step 2: Subscribing to Channels

    There are three types of channels in Pusher: Public, Private, and Presence.

    • Public channels: These channels are public in nature, so anyone who knows the channel name can subscribe to the channel and start receiving messages from the channel. Public channels are commonly used to broadcast general/public information, which does not contain any secure information or user-specific data.
    • Private channels: These channels have an access control mechanism that allows the server to control who can subscribe to the channel and receive data from the channel. All private channels should have a private- prefixed to the name. They are commonly used when the sever needs to know who can subscribe to the channel and validate the subscribers.
    • Presence channels: It is an extension to the private channel. In addition to the properties which private channels have, it lets the server ‘register’ users information on subscription to the channel. It also enables other members to identify who is online.

    In your application, you can create a subscription and start listening to events on: 

    // Here my-channel is the channel name
    // all the event published to this channel would be available
    // once you subscribe to the channel and start listing to it.
    
    var channel = pusher.subscribe('my-channel');
    
    channel.bind('my-event', function(data) {
      alert('An event was triggered with message: ' + data.message);
    });

    CODE: https://gist.github.com/velotiotech/d8c27960e2fac408a8db57b92f1e846d.js

    Step 3: Creating Channels

    For creating channels, you can use the dashboard or integrate it with your server. For more details on how to integrate Pusher with your server, you can read (Server API). You need to create an app on your Pusher dashboard and can use it to further trigger events to your app.

    or 

    Integrate Pusher with your server. Here is a sample snippet from our node App:

    var Pusher = require('pusher');
    
    var pusher = new Pusher({
      appId: 'APP_ID',
      key: 'APP_KEY',
      secret: 'APP_SECRET',
      cluster: 'APP_CLUSTER'
    });
    
    // Logic which will then trigger events to a channel
    function trigger(){
    ...
    ...
    pusher.trigger('my-channel', 'my-event', {"message": "hello world"});
    ...
    ...
    }

    CODE: https://gist.github.com/velotiotech/6f5b0f6407c0a74a0bce4b398a849410.js

    Step 4: Adding Security

    As a default behavior, anyone who knows your public app key can open a connection to your channels app. This behavior does not add any security risk, as connections can only access data on channels. 

    For more advanced use cases, you need to use the “Authorized Connections” feature. It authorizes every single connection to your channels, and hence, avoids unwanted/unauthorized connection. To enable the authorization, set up an auth endpoint, then modify your client code to look like this.

    const channels = new Pusher(APP_KEY, {
      cluster: APP_CLUSTER,
      authEndpoint: '/your_auth_endpoint'
    });
    
    const channel = channels.subscribe('private-<channel-name>');

    CODE: https://gist.github.com/velotiotech/9369051e5661a95352f08b1fdd8bf9ed.js

    For more details on how to create an auth endpoint for your server, read this. Here is a snippet from Node.js app

    var express = require('express');
    var bodyParser = require('body-parser');
    
    var app = express();
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: false }));
    
    app.post('/pusher/auth', function(req, res) {
      var socketId = req.body.socket_id;
      var channel = req.body.channel_name;
      var auth = pusher.authenticate(socketId, channel);
      res.send(auth);
    });
    
    var port = process.env.PORT || 5000;
    app.listen(port);

    CODE: https://gist.github.com/velotiotech/fb67d5efe3029174abc6991089a910e1.js

    Step 5: Scale as you grow

     

    Pusher comes with a wide range of plans which you can subscribe to based on your usage. You can scale your application as it grows. Here is a snippet from available plans for mode details you can refer this.

    Image Source: Pusher

    Conclusion

    This article has covered a brief description of Pusher, its use cases, and how you can use it to build a scalable real-time application. Using Pusher may vary based on different use cases; it is no real debate on what one can choose. Pusher approach is simple and API based. It enables developers to add real-time functionality to any application in very little time.

    If you want to get hands-on tutorials/blogs, please visit here.