Skip to content
Titanium SDK

Layout & positioning

Written for Titanium SDK 13.4.0 and later

Titanium positions a view from properties you set on it: top, left, right, bottom, center, width, height. Which of them apply depends on one property on the parent: its layout.

There are three modes, and the default is the one people trip over.

The three layout modes

composite is the default. Children are pinned to the parent's edges, and a child with no positioning properties at all is centred.

vertical stacks children top to bottom in the order you add them.

horizontal places them left to right, wrapping onto a new row when they run out of width.

const win = Ti.UI.createWindow({ backgroundColor: 'white' });

const stack = Ti.UI.createView({
  layout: 'vertical',
  top: 40,
  width: '80%',
  height: Ti.UI.SIZE
});

stack.add(Ti.UI.createLabel({ text: 'First', top: 10 }));
stack.add(Ti.UI.createLabel({ text: 'Second', top: 10 }));
stack.add(Ti.UI.createLabel({ text: 'Third', top: 10 }));

win.add(stack);
win.open();

Three parents side by side holding the same four coloured squares. Under composite only one square is visible, because all four are pinned to the same spot and the last one added is on top. Under vertical they stack in a column. Under horizontal they run left to right and wrap onto a second row.

layout is set on the container, never on the child. A child cannot ask to be stacked; its parent decides.

Sizing: SIZE, FILL, and numbers

width and height each take one of four kinds of value.

Value Meaning
Ti.UI.SIZE As large as the content needs, and no larger
Ti.UI.FILL As large as the parent allows
A number That many units. 120 is 120dp with the template's default unit
A string An explicit unit or a percentage: '120dp', '50%'

A plain number is in whatever ti.ui.defaultunit says in tiapp.xml, which ti create sets to dp. Density-independent pixels, so 120 is the same physical size on any screen. Write '120px' if you really mean device pixels, which you rarely do.

Important

Ti.UI.SIZE on a container measures its children. A container with height: Ti.UI.SIZE and no children is zero tall and invisible, which is the usual reason a view you definitely added is nowhere on screen.

What a view does when you say nothing

Leave width and height unset and each view falls back to its own default, which is SIZE for things that wrap content and FILL for things that own a region:

View Default width Default height
Label, Button, Switch, ImageView SIZE SIZE
ActivityIndicator, Picker SIZE SIZE
TextField, TextArea, SearchBar FILL SIZE
Slider, ProgressBar, Toolbar FILL SIZE
ScrollView, ScrollableView, WebView FILL FILL
ListView, TableView, TabGroup FILL FILL

Read off the iOS layout code, where these are declared. Android reaches the same answers through each native widget's own measurement rather than a declared default, so treat the table as the shape of the behaviour and set width and height yourself wherever it matters.

A plain View declares neither. Give it a size, or give it children and Ti.UI.SIZE.

Composite layout, and why your property was ignored

In composite layout you can over-specify a view: height, top, center and bottom can all be set, and they cannot all be honoured. Rather than erroring, Titanium applies a fixed precedence and silently drops the rest.

Vertically the order is height, top, center.y, bottom. The two highest-precedence properties you set are the two that win.

What you set What happens
height and top Positioned top from the parent's top, at that height. center.y and bottom ignored
height and center.y Centred on center.y, at that height. bottom ignored
height and bottom Positioned bottom from the parent's bottom, at that height
top and bottom Both edges pinned. Height is whatever is left between them
top and center.y Top edge pinned, centre pinned, height implied. bottom ignored
top only Pinned from the top, at the view's default height
bottom only Pinned from the bottom, at the view's default height
none of them Centred vertically, at the view's default height

Horizontally it works the same way, with the order width, left, center.x, right.

That table is the answer to most "why is my view the wrong size" questions. Setting top, bottom and height does not stretch anything: height outranks bottom, so the view sits at the top with the height you asked for. Drop the height and the two pins size it.

Two panes side by side on an iPhone. In the left pane a blue view with top, bottom and height all set sits 100 points tall against the top of its grey parent, leaving the rest of the parent empty below it. In the right pane the same view with no height fills the parent, stopping 20 points short of each end.

Vertical layout

Children stack in the order added. A child's top is the gap above it and its bottom is the gap below, so the space between two children is the upper one's bottom plus the lower one's top.

const win = Ti.UI.createWindow({ backgroundColor: 'white', layout: 'vertical' });

win.add(Ti.UI.createLabel({ text: 'Title', top: 60, left: 20 }));
win.add(Ti.UI.createLabel({ text: 'Subtitle', top: 8, left: 20 }));
win.add(Ti.UI.createView({ height: 1, width: Ti.UI.FILL, top: 16, backgroundColor: '#ccc' }));

win.open();

Horizontally each child still behaves as it would in composite layout, which is why left: 20 works above and why a child with no horizontal properties is centred.

Warning

A child with height: Ti.UI.FILL in a vertical layout takes everything left over, and every child after it gets nothing. One FILL child per vertical container, and put it last unless you mean to push the rest off screen.

Horizontal layout

Children run left to right. Wrapping is on by default: when a child does not fit in the current row it starts a new one, and each row is as tall as its tallest child.

const tags = Ti.UI.createView({
  layout: 'horizontal',
  top: 40,
  width: 240,
  height: Ti.UI.SIZE
});

for (const name of ['alloy', 'android', 'ios', 'layout', 'listview']) {
  tags.add(Ti.UI.createLabel({
    text: name,
    width: Ti.UI.SIZE,
    height: Ti.UI.SIZE,
    left: 4,
    right: 4,
    top: 4,
    bottom: 4,
    color: '#fff',
    backgroundColor: '#4a4a4a'
  }));
}

Five dark chips labelled alloy, android, ios, layout and listview. The first four fill the row and listview has wrapped onto a second row below them.

Give each child a size. A label with no width fills the row rather than shrinking to its text, and five of them stack into one dark block instead of five chips. Ti.UI.SIZE is what makes a child as wide as its content, and it is the first thing to check when a horizontal layout comes out wrong.

Within a row, a child with neither top nor bottom is centred; with one of them it aligns to that edge with that much padding; with both, the two are treated as padding.

Set horizontalWrap: false and the children stay on one line instead, with left and right acting as the gaps between them.

Overlap and stacking

Composite layout lets children overlap. Later children draw on top of earlier ones, and zIndex overrides that for siblings:

const badge = Ti.UI.createView({
  width: 20,
  height: 20,
  borderRadius: 10,
  backgroundColor: 'red',
  top: -6,
  right: -6,
  zIndex: 1
});

Negative pins put a child outside its parent's bounds, which is how a badge sits on a corner. On Android a child drawn outside its parent may be clipped, so give the parent enough room rather than relying on the overflow.

Reading a size back

width and height are what you asked for, not what happened. A view whose width is Ti.UI.FILL reports Ti.UI.FILL, not a number. For the measured result use rect or size, and only after the view has been laid out:

view.addEventListener('postlayout', () => {
  Ti.API.info(`${view.rect.width} x ${view.rect.height}`);
});

postlayout fires whenever the view is measured, which is more than once. If you only want the first, remove the listener inside it.

Common mistakes

A container sized Ti.UI.SIZE with no children is zero by zero. Nothing is wrong with the children you add later; they are inside a box with no area.

top and bottom with a height does not stretch. See the precedence table above.

Two Ti.UI.FILL children in a vertical layout. The first takes the remaining space and the second has none.

Setting layout on the child. It controls that view's own children, not where it sits in its parent.

A percentage against a Ti.UI.SIZE parent. '50%' of a parent that is sizing itself to its children is not a number anyone can compute, and the result is not useful. Give the parent a real size first.

Next

Lists and tables covers the two scrolling list types and which to use.

Edit this page on GitHubLast updated Nov 9, 2026