Write your own flexible custom border image component in react native

After talking about my Box component in previous articles, I was asked repeatedly to make a quick writeup about how I built it. In the end, the setup for using it isn’t quite as simple as I thought it might turn out when I started this project.

As you can’t simply use 9-Patch images / capInsets for this kind of flexibility when using custom and complex border frame images, a lot of styles and stuff is needed to get it working. Maybe going through the process of designing this component makes it a bit clearer how to use it.

If you want to code along, you can either clone/download my expo-playground project from GitHub at this tag or start a new project from scratch.

My code examples will be based on the expo-playground so you might need to download a few image files from my project or create your own.

Let’s start by making a new component that will serve as our playground for this tutorial. I’m calling it Box (yeah, I’m that creative…) and import it into our ScenePlayground.jsx

We’ll also add a few base styles to make the Box outlines visible and test our setup. Pass down the children nodes as a prop and we’re done here.

If you run npm start now and open the project on your emulator or test device, you’ll see our simple Box with black border, white background and our child text component.

As I had promised you a flexible solution, we need to allow the user to initialise the component with custom settings which we’re gonna pass to the props as a setup object.

Inside our component, we add the new prop to the prop-types and grab its attributes via destructuring. By replacing those with a default value added by a logical OR we can update our component styles accordingly.

I’ve added a defaultSetup via Box.defaultProps in case the setup prop is missing when Box is used.

It’s now getting a bit more complex and tedious as we need to create and iterate over 8 pieces for the 4 sides and corners. To keep it short and easy to read, I’m using the cardinal directions. N, S, E and W for the sides and NE, NW, SE and SW for the corners.

We are going to map over our array of cardinal directions and as we’re going to need the name and object of each, I’m adding an id attribute to each, containing the name of the object as a string.

There are two alternatives to including those id attributes. We could iterate over an array of strings instead of objects and then map them to the object in question but I’m not a big fan of eval() and it’s newer alternative, not even hidden deep within my own code with no possible harmful user input anywhere near it. Alternatively, we could keep our object array and get the key names of our key/value pairs but in the end it’s much more easy, faster and especially more readable if you simply include and use the name of the object as a string in this case.

Let’s spread our Box’s boxes a bit and arrange them accordingly to their names. For this I’ve already given the box itself the display flex style and centered it’s content. We can now easily position all elements with a few simple and selected rules using absolute positioning, top/bottom/left/right and alignSelf: ‘center’.

Don’t forget to replace our temp styles with the styles of our respective cardinal element.

We’ve made a teeny tiny error when we started earlier. If you would assign 0 as a value for margin for example, 0 wouldn’t be treated as a margin of zero as our logical OR ( the 2 || in our code) treat the number value 0 as nullish or false in this case and ignore our attribute of 0 as they default to the alternative margin of 10. We can change that by using ?? instead of ||. This is called a null coalescing operator, something well known in other languages and with ES2020 this feature is also available for us in Javascript.

As this is only included in ES2020, make sure to check your Babel (or Metro or any other transpiler you might be using) to know and understand ES2020.

As you can see, our background is now bleeding out and visible on all sides even beyond our frame’s borders. Let’s fix that.

To facilitate the new backgroundOffset style, we’ll add it to our setupOffset. While the idea might seem a bit abstract and artsy right now while working with simple boxes, this use case will be clearer once we add the more complex border images in the end of this tutorial.

We’ve seen that the offset style given by the user is working so let’s get rid of it in the setup for now and adjust our component to use either the setup or the respective borders’ thickness depending on the given props.

This way user styles will win but if missing, our component will default to the known border thickness.

It’s time to add the actual visuals and get rid of our temp. styles and borders.

For this step, you need to grab the demoFrameFubar assets from my project here. If you follow that link, you should find a folder with 8 images and an index.js file. Take all of those including the index file and save them in your own asset folder in the project. We won’t be using the index file right now but it will be useful later on.

Import all images from the assets and use the cardinal directions again when naming the imported files.

While we’re at it, let’s update the setupSimple AND setupOffset with our newly imported images and new dimensions. Quick tip, both are identical, so copy n paste to save some time here.

Ain’t that pretty? … you gotta love programmers art!

I know I know… it was mean of me to let you type all those Ns and Es with names and their size and images… but I wanted to drive home an important point here. Doing something like this is tiresome and error-prone. If you are working with larger numbers of images, maybe using an index file might be a good idea. Luckily you already downloaded it for our previous step so let’s start using it.

Kick out all those cardinal direction attributes and image imports and replace them by importing the index file from the asset folder and spread it into our setups.

I cut off this one, it’s repetitive… don’t forget the setupOffset!

Ok, I’ve prepared 2 more asset folders for other frames. Go ahead and download those too:

We’ll start with the comic one for now and I’ll explain why in a moment. Also for now only change one of our two boxes to use the new assets while the other one should continue to use the old Fubar frame.

If you hit save and preview the result… rest assured that it is not entirely your fault that your screen might look like a jumbled mess of sprites right now. When we imported our new frame images, we didn’t check the size and apparently the comic frame is much larger. We can’t use it like this but maybe we can scale it down a bit. If you were perceptive enough you might have spotted the “scale: 0.2" line in our last step. Currently, this doesn’t do anything so let’s go back to our component and add an option to scale things down (or up) as defined in our setup.

Remember when I mentioned that the offset example would be a bit clearer in the end? Our third asset folder contains the warning sign frame. In this example, we don’t have 4 sides that are connected ad the corners by 4 corner pieces. But I’m getting ahead of myself.

As you can see, in our last example the E and W sides are inset a bit and the N and S borders stretch to the very end of our frame. Our corner pieces are partly transparent and only add a boundary and some grunge to our top and bottom warning bars.

You can find the finished code on GitHub. I’ve created a tag for this tutorial here: expo-playground v1.1 Finished Box tutorial

This setup is quite powerful as it allows us to use lots of variations with different combinations. If you add a few decorations on top and a nice background, you can create lots of beautiful Boxes.

Working with the Box in projects

When starting a new project or sometimes even for quick prototyping, I usually import my Box Component and create a set of components using the Box under the surface. That way I can create all the variations I need with one tool and use them wherever I need them in my code. That way, all the setup work with source images and positioning are centralised and I keep my actual template files cleaner, shorter and easier to read.

If you want to use the box or one of my other reusable components in your projects, I’m currently finalising the first release of my rn-gui framework and over the next couple of weeks, I’m gonna add more of my home brewed elements to the framework, one piece at a time, as I still need to add tests, documentation and such before I release them as production-ready to the public. I will set up a GitHub page providing an overview of planned and implemented features and solutions and if you are a patreon of mine, you can even vote on the priority of the planned components or add suggestions to the backlog.

Some words about me:

If you want to see more of my work and progress, feel free to check out my series on building a React Native Web App with Hooks, Bells, and Whistles with Expo SDK33. I’m also currently working on a new series about building more complex and scalable apps using React/Redux, where I’ll go into details about how and why I do stuff the way I do as well as some articles on my experience in building games for web and mobile with React.

And if you feel really supportive right now, you can always support me on patreon, thus allowing me to continue to write tutorials and offer support in the comments section.

I’m a Web / App Developer & father 👨‍👩‍👧 doing freelance and part-time agency work since 2003, 💻 building stuff on the side 🕹 and attending conferences 🎟