Here at Clock our developers are at the forefront of technology, ensuring that clients have great products that provide exceptional user experience. But behind the glossy interface, lies an ability to ensure that developers are constantly reacting and adapting to the needs of the users, with the best software available.
As we go “behind the app”, we caught up with Ryan Murthick about his experiences with creating high performing cross platform apps – and the UI tech that’s making it happen.
Can you tell us a little bit about the frameworks you have worked with previously?
Mobile apps are something we’ve done at Clock for many years now – linking physical and digital experiences. Just a few examples include the Colooder fan experience platform, Sun Savers and the Leicester Tigers app.
Whilst this method can save development time, it does have its limitations. For instance, although native code can be used, any sort of native UI elements can be much harder to use.
For an upcoming project, we wanted to be able to use native code to give us the full power of native UI elements that Cordova simply didn’t have decent support for.
So what do you do to resolve this?
The obvious choice for this was React Native. This is because it allows us to still use our existing React knowledge. Rather than just creating html elements, it creates native platform specific applications that uses the native UI controls of the OS rather than a web view. With ReactNative Web those same elements can be used to still output html element on the web.
This is only one piece of the puzzle though, as the development model of a web app is quite different to that of a native app even if the UI can be shared.
So what did you choose to help complete that puzzle?
We chose to use Solito. This is a new framework that allows us to get the benefits of full native application UI on mobile, whilst still using the latest modern web development frameworks like next.js.
At its core, Solito is just a set of helper methods for bridging the gap between the url based navigation of web apps and the screen based navigation of native apps. By using their recommended monorepo based project structure though, it allows you to have all the shared application code in one package, which is then imported by a React Native app for mobile devices, and a next.js application for the web.
Can you give us some benefits of the framework?
99% of the application code lives in the shared project, leaving the next.js and the React Native projects to be used for defining web routes and setting up any native code needed for the app to function. This allows for much more code reuse which means, hopefully, less development time.And if some react native library doesn’t work on the web, it can be swapped out by making a web.tsx file. Additionally, because Solito uses next.js on the web, we get all of the benefits that next.js brings. This includes Single sign on ability, Server-side rendering for frequently updated data without compromising on the SPA (single-page application) experience.
Another benefit of the Solito approach is that you don’t need to use any 3rd party native modules. It is also possible to test on native devices without having to build an app for the devices. Expo allows you to use the Expo Go app to test react native applications on a mobile device with no native build required, as long as you can stick to JS only libraries.
Can you tell us a little bit about how you choose a UI framework?
If I can take one of our recent projects as an example, we originally went for NativeBase for the UI. This seemed like a great option at the time – the library has been around for a while now and is very mature. It offers a great default set of UI components we could extend and theme to create our app without having to reinvent the wheel, as well as, being well supported in both the web and natively in theory.
But as we know, theory and practice can be two different things entirely?
Yes, exactly! As we were getting stuck into development and building out UI components and pages, we noticed the web performance was getting worse and worse. This wasn’t replicated in the app, so we spent a day digging into the performance issues to see what was up.
We found that NativeBase was spending a lot of time in this file calculating the styles for each component every time the page rendered or re-rendered, which meant page loadsand page transitions on the web would be slower and slower as more things we re-added to the page. This would dramatically impact user experience.
We weren’t the only ones experiencing these issues. A long running issue on the Github page suggests that there’s many others with the same problems, and although there has been attempts to fix it, these aren’t yet stable. When we tried a couple of them they either didn’t help, or we couldn’t get them to work, so we had to totally rethink the UI choice.
So what do you do as developers in this situation?
We did a little digging and found a library called Tamagui. This is a relatively new library that’s still in beta, but has a number of innovations over NativeBase. Predominantly, the focus is on performance which means that before we were seeing over 50 seconds of blocking JS execution just to render the front page, this was reduced to less than 3 seconds in a development environment.
This is largely due to the differences in how the two libraries handle styling. NativeBase tries to handle everything at run time, causing large complex component trees to suffer major performance issues when complex styles are used. Tamagui on the other hand, has a custom compiler that takes the JS used to evaluate styles and where possible calculates everything at build time, and depending on the platform output styles as appropriate (i.e.CSS on the web and react native styles on mobile). Tamagui also flattens the dom output on the web where appropriate, which further improves performance.
So did you have any issues with the migration?
At this stage, we were still relatively early in development, but that doesn’t mean we hadn’t already built a number of UI components.
One of the major pain points of this migration was having to not only move UI libraries, but also upgrade to a newer version of ReactNative, and make the relevant changes to our native projects to be able to do so.
We took the approach of doing a hard removal of NativeBase. We took it out of the project and reverted back to placeholder pages whilst getting a basic app build again on all 3 platforms. We then started porting over our old NativeBase components into new Tamagui components.
This took some time but was an essential build phase to get right. Having made the upgrades, we had the app running again on all 3 platforms with Tamagui elements rendering instead of NativeBase elements.
From a developer’s viewpoint, what did you find to be the main differences between the 2 UI libraries?
In NativeBase, making custom components is generally a case of customising the given components in NativeBase, or if you need to style a fully custom component to use the Factory to make it support the styling and theming props.
Tamagui is similar, except it exports a styled helper which can be interpreted at build time in order to optimise the styles of the components.You’ll also have to do this more often – elements that are otherwise included out of the box in NativeBase generally need to be created from scratch in Tamagui. In this sense, Tamagui is more of a framework to build components, although some commonly used components are included.
What about coding differences you found?
Although both UI libraries define utility props on both the in-built components and ones made using either of the factories they include, the syntax of them isn’t identical. For example:
HStacks and VStacks – These are called XStacks and YStacks in Tamagui, but otherwise perform the same task of stacking the contents horizontally or vertically.
In NativeBase you could use an inline array on some props to define UI based on breakpoints e.g. the following will apply progressively larger sizes for the font as the page width gets bigger
In Tamagui, breakpoints are handled in a more verbose fashion, which allows for the compiler to better optimise it. For instance, in the following example, the font size is increased when the sm breakpoint is hit:
These two examples also demonstrate the difference between how the two frameworks handle themed variables – in Tamagui themed variables are distinguished with the $ in front of them, with the variables being resolved to tokens where appropriate.
Another thing to watch out for is that things that may have just worked in NativeBase will not work in Tamagui – one of those being px sizings. Styling in general is closer to what React Native natively provides. So unless it’s a percentage or a device independent unit, it’s unlikely to work.
Did you find many challenges with Tamagui?
There’s still a few pain points with Tamagui that have yet to be resolved, and although these aren’t as bad as the performance issues we were seeing with NativeBase, they do pose some challenges to overcome.
The key one for us is the Tamagui UI elements that are not yet fully supported on native platforms – one of the main ones being the select box, which is currently only fully working on the web in both classic select form and sheet form. We’ve additionally had issues with it on android even in sheet form.
Another general pain point is that monorepos in general aren’t always well supported by all libraries, and sometimes installing a new library can make the varying node_modules in the development tree go out of sync. For us, we defined a yarn clean command to essentially wipe and reinstall these.
Finally, what are your thoughts going forwards on the future of UI?
Being on the cutting edge of single code base applications means we get much more flexibility when using JS for mobile applications for clients to use native UI modules and patterns that would otherwise not be available to us. But the ecosystem is still quite new and it comes with a number of pain points, which can make development a bit more difficult when those are encountered. There is a lot of progress though, and things are getting better very quickly. We believe this will be a great way of making applications that look great and perform brilliantly on both mobile and web, whilst saving development time by not having to maintain separate applications.