Skip to content

Modus-Logo-Long-BlackCreated with Sketch.

  • Services
  • Work
  • Blog
  • Resources

    OUR RESOURCES

    Innovation Podcast

    Explore transformative innovation with industry leaders.

    Guides & Playbooks

    Implement leading digital innovation with our strategic guides.

    Practical guide to building an effective AI strategy
  • Who we are

    Our story

    Learn about our values, vision, and commitment to client success.

    Open Source

    Discover how we contribute to and benefit from the global open source ecosystem.

    Careers

    Join our dynamic team and shape the future of digital transformation.

    How we built our unique culture
  • Let's talk
  • EN
  • FR

There aren’t too many things as satisfying as doing something in less than half the lines of code than if we were being sloppy. Case in point, we’ll be exploring how to write custom components in Nativescript to make our code reusable and DRY (as in “Don’t Repeat Yourself”, not the absence of moisture). Let’s dive right in.

Setup

Feel free to clone the repo from github which includes all the examples we’ll go through here. Or, if you are that kind of person, create a new project from scratch with:

tns create mycomponents
cd mycomponents
tns platform add ios
mkdir app/components
tns livesync ios --emulator --watch

All the components we’ll create will reside in app/components.

A simple static component

Sometimes, we find ourselves writing the same UI markup in more than one XML file. Examples of such situations may include sidebar, action bar (title bar), copyright info, etc. In that case, all we really need is to park that repeated XML somewhere and reference it. Here’s a quick example on how to do that:

<!-- app/components/copyright/copyright.xml> -->

<Label text="© 2016 Akash Agrawal"/>

And this is all it takes to create a bare-bones component. Now, we can place this in our xml view.

<Page xmlns="http://schemas.nativescript.org/tns.xsd"
    xmlns:mycomponent="components/copyright"
    navigatingTo="onNavigatingTo">
    <StackLayout id="container">
        <mycomponent:copyright/>
    </StackLayout>
</Page>

There are several important points to note here:

  • We need to remember that whenever we want to include a custom component in our view, we need to create a namespace for it. It’s the second line in the above example.
  • The path we provide in that namespace is relative to app directory and should point to the directory in which our component XML and JS (if we’ve created it) files are located.
  • When actually referencing the component, we follow the syntax of <namespace:component/> where namespace is the name we gave to the path (again, second line above) and component is the filename (without extension) of our component XML. As a tip, there’s no problem naming your namespace to be the same as your component, and then placing it like <copyright:copyright/>. The above nomenclature is just to differentiate what’s what.

Dynamic component without arguments

Another use case is that we want a fully independent component which also has some behavior attached to its XML. An example of this may be a simple clock that we might want to place somewhere in our project. In this case, XML alone will not suffice since we’ll need to update the text with the current time. Fortunately, it’s dead simple to attach behavior with XML. We just have to create a javascript (or typescript) file with the same name as the component XML. Observe:

<!-- app/components/clock/clock.xml -->

<Label loaded="onLoad"/>
/* app/components/clock/clock.js */

exports.onLoad = args => {
    const label = args.object;

    setInterval(() => {
        label.text = new Date().toString();
    }, 1000);
};

And we place it in our page the exact same way we did for our static component. Nativescript automatically searches for a javascript (or typescript) file if it’s present with the same name and in the same location as the XML file and attaches them together.

<Page xmlns="http://schemas.nativescript.org/tns.xsd"
    xmlns:clock="components/clock"
    navigatingTo="onNavigatingTo">
    <StackLayout id="container">
        <clock:clock/>
    </StackLayout>
</Page>

Providing arguments to components

This is where it gets interesting. Most of the good stuff that we’ll likely use the components for involves them taking some arguments from the page. This too, however, is fairly straightforward. Code or it didn’t happen, right?

<!-- app/components/greeter/greeter.xml -->

<StackLayout loaded="onLoad">
    <Label id="fL"/>
    <Label id="nL"/>
</StackLayout>
/* app/components/greeter/greeter.js */

exports.onLoad = args => {
    const container = args.object;

    const frameworkLabel = container.getViewById('fL');
    const nameLabel = container.getViewById('nL');

    frameworkLabel.text = `Hello ${container.framework || 'Nativescript'}`;
    nameLabel.text = `My name is ${container.name.first} ${container.name.last}`;
};

Notice how all the arguments we provided are exposed on the root view of our component. If we have multiple views at the root of our component’s XML, only the last view counts. All the others before it are as good as absent. They will not trigger any load events, won’t appear on page (even if you load static content into them) and don’t receive any arguments. To prevent confusion, we should always have only one view at the root. If we need more than one, group them under a layout (that way, the layout becomes the root).

Here’s how we reference them from our page:

<!-- app/main-page.xml -->

<Page xmlns="http://schemas.nativescript.org/tns.xsd"
    xmlns:greeter="components/greeter"
    navigatingTo="onNavigatingTo">
    <StackLayout id="container">
        <greeter:greeter framework="angular-nativescript" name="{{name}}"/>
    </StackLayout>
</Page>
/* app/main-page.js */

exports.onNavigatingTo = args => {
    const page = args.object;
    page.bindingContext = {
        name: {
            first: 'Akash',
            last: 'Agrawal'
        }
    };
};

We could have set framework inside the binding context too and passed it all as one argument instead of two. The example above demonstrates how to pass simple strings (the framework argument) right from the XML.

Loading custom components from javascript

Up till now, we’ve seen how we can use our components by declaring them under a namespace and referencing them with their filename. What if we want to add the component to our layouts directly from javascript? Use cases for this may include adding a profile image component after the user has loaded. Note that there are ways to do this using the usual XML way and Observables. Still, contrary to what memes will have you believe, there’s no such thing as knowledge overload.

We’ll use the same greeter component from the above example. No changes to it whatsoever are required. The modifications will be where and how we use it.

<!-- app/main-page.xml -->

<Page xmlns="http://schemas.nativescript.org/tns.xsd"
    navigatingTo="onNavigatingTo">
    <StackLayout id="container">
    </StackLayout>
</Page>

Notice that we’re no longer using the namespace. That’s because we’ll insert the component purely from javascript. However, we’ve given an id to our layout to make it easy for us to insert the component at the right place. Here’s the required js code:

/* app/main-page.js */

const builder = require('ui/builder');

exports.onNavigatingTo = args => {
    const page = args.object;
    const myName = {
        first: 'Akash',
        last: 'Agrawal'
    };
    const container = page.getViewById('container');

    const greeter = builder.load({
        path: 'components/greeter',
        name: 'greeter'
    });

    container.addChild(greeter);

    greeter.framework = 'angular-nativescript';
    greeter.name = myName;

};

Easy-peasy. We use the load method from ui/builder. The path property points to the name of the directory and the name property specifies the file name (without extensions). Nativescript will take care of loading XML and JS from it. It returns an instance of the View class on which we can set any attributes we might want to pass. We then add it to container to actually place it in UI.

Component in Component

This works exactly how we’d guess it would. The following is a component called greeterTime which is exactly the same as the greeter above, except that it also has the clock component we saw earlier. The only change is in the XML.

<!-- app/components/greeterTime.xml -->

<StackLayout loaded="onLoad"
    xmlns:clock="components/clock">
    <Label id="fL"/>
    <Label id="nL"/>
    <clock:clock/>
</StackLayout>

Of course, we can also insert it from javascript using the same method we saw in the previous section.

Communication with Components using Observable

From the examples above, we’ve already established that we can easily pass whatever we wish to our components. However, there may be situations where we want to keep our data from the parent and component in sync. Or, at the very least, be notified if data changes in any place so we can take some action. We can easily do this using the Observable class. The example below creates a simple two field name form component. The objective is to keep the data in UI of parent and child component in sync with each other.

<!-- app/components/nameForm.xml -->

<StackLayout loaded="onLoad">
    <TextField text="{{ first }}" />
    <TextField text="{{ last }}" />
</StackLayout>
/* app/components/nameForm.js */
exports.onLoad = args => {
    const container = args.object;

    container.bindingContext = container.fullName;
};
<!-- app/main-page.xml -->

<Page xmlns="http://schemas.nativescript.org/tns.xsd"
    xmlns:nameForm="components/nameForm"
    navigatingTo="onNavigatingTo">
    <StackLayout id="container">
        <StackLayout orientation="horizontal">
            <Label text="{{name.first}}" />
            <Label text="{{name.last}}" />
        </StackLayout>
        <nameForm:nameForm fullName="{{name}}"/>
    </StackLayout>
</Page>
/* app/main-page.js */

const Observable = require('data/observable').Observable;

exports.onNavigatingTo = args => {
    const page = args.object;
    const myName = {
        first: 'Akash',
        last: 'Agrawal'
    };

    page.bindingContext = {
        name: new Observable(myName)
    };
};

And that’s it. We’ve got a set of labels in our main page, each pointing to a part of the name. In our custom component, instead of labels, we’ve got two text fields to edit the name. Since we’ve wrapped the name in an Observable, editing any text field in the component will automatically update the label in the main page too. For information on more cool stuff we can do with Observable, including subscribing to change events on properties, Nativescript’s official docs are a good place to start.

Conclusion

I am really grateful to Nathanael Anderson for taking the time to answer my questions about adding components dynamically via javascript. He was extremely helpful and kind in getting this stuff pushed through my skull.

There’s a lot of cool stuff we can do with Nativescript. Hopefully this post will help you understand how to isolate that cool stuff and use it the right way. Use the comments below for any suggestions, corrections or general discussion.

Posted in Application Development
Share this

Akash Agrawal

Akash Agrawal is a Software Engineer with Modus Create. He is an experienced JavaScript and Ruby programmer with chops in front and backend development along with database architecture. Akash has a deep interest in pure functional languages and is a padwan haskell hacker. He enjoys exploring surrounding areas on his bike when not working.
Follow

Related Posts

  • Lazy Loading Components with ComponentLoader - Menu
    Lazy Loading Components with ComponentLoader

    The Ext.ComponentLoader is perhaps one of the least discussed and lesser known features that ExtJS…

  • Lazy Loading Components with ComponentLoader - Menu
    Lazy Loading Components with ComponentLoader

    The Ext.ComponentLoader is perhaps one of the least discussed and lesser known features that ExtJS…

Want more insights to fuel your innovation efforts?

Sign up to receive our monthly newsletter and exclusive content about digital transformation and product development.

What we do

Our services
AI and data
Product development
Design and UX
Modernization
Platform and MLOps
Developer experience
Security

Our partners
Atlassian
AWS
GitHub
Other partners

Who we are

Our story
Careers
Open source

Our work

Our case studies

Our resources

Blog
Innovation podcast
Guides & playbooks

Connect with us

Get monthly insights on AI adoption

© 2025 Modus Create, LLC

Privacy PolicySitemap
Scroll To Top
  • Services
  • Work
  • Blog
  • Resources
    • Innovation Podcast
    • Guides & Playbooks
  • Who we are
    • Our story
    • Careers
  • Let’s talk
  • EN
  • FR