Alloy vs Classic Titanium: Choosing an App Structure That Scales
Deciding between Alloy MVC and classic Titanium SDK? This guide compares maintainability, build complexity, and team workflow, with a concrete login-screen example to validate your choice.
04 Jul 2026, 00:28 UTC

The decision is about maintainability, not speed
Choosing between Alloy MVC and classic Titanium SDK is not a performance decision. Both compile to the same native Titanium runtime, and after the build step, startup and execution differences are negligible. The real choice is about how your team writes, reviews, and maintains UI code as the app grows from a single screen to a multi-screen product.
What each approach actually does
Alloy is the Model-View-Controller (MVC) framework built into Titanium. It splits UI into XML view files, styling into TSS (Titanium Style Sheet) files, and logic into JavaScript controllers. This separation lets designers adjust layout without touching application logic, and it reduces the boilerplate needed for common patterns such as forms and lists.
Classic Titanium uses imperative JavaScript calls—Ti.UI.createWindow, Ti.UI.createTextField, and similar—directly in your controller or app.js file. You get full control over every property, but UI construction and business logic live in the same file.
Side-by-side comparison
| Aspect | Alloy MVC | Classic Titanium |
|---|---|---|
| UI definition | XML views + TSS stylesheets | Imperative JS (Ti.UI.create*) |
| Code separation | Views, styles, and logic in separate files | UI and logic mixed in JS |
| Build step | Extra Alloy compile layer | Direct Titanium compile |
| Maintainability | Better for large, complex UIs | Risk of duplicated layout code |
| Team workflow | Designers can edit UI without JS | Requires JS knowledge for all changes |
| Best fit | Multi-screen apps, teams with designers | Prototypes, small utilities |
Trade-offs that matter
Alloy introduces an extra build layer. If the Alloy CLI and Titanium SDK versions are mismatched, compilation can fail before you ever see the app. Classic code avoids that layer, which makes it slightly faster to start and simpler to debug for a single-screen prototype. However, as UI complexity grows, classic code tends to accumulate duplicated layout properties across files, increasing the risk of inconsistent styling and harder refactors.
Alloy's XML views are declarative: you describe what the UI should contain, and the compiler generates the corresponding Titanium objects. That makes it easier to spot layout structure at a glance. Classic code is imperative: you call creation functions and set properties one by one. The imperative style offers full control, but it also means every visual change requires a code change.
Alloy also supports automatic binding of model data to views through XML attributes, which removes repetitive event listeners and manual property updates for lists and forms. That advantage is small for a two-field login screen and large for a data-driven dashboard.
Concrete example: a login screen in both styles
The following example builds a simple username/password form with a login button. The behavior is identical: tapping the button with an empty field shows an alert.
Alloy implementation
Create three files under app/.
app/views/login.xml:
<Alloy>
<Window class="loginWindow">
<TextField id="username" hintText="Username" />
<TextField id="password" hintText="Password" passwordMask="true" />
<Button id="loginBtn" onClick="doLogin">Log In</Button>
</Window>
</Alloy>
app/styles/login.tss:
".loginWindow": {
backgroundColor: "#fff",
layout: "vertical"
},
"#username": {
top: 20,
width: "80%",
borderStyle: Ti.UI.INPUT_BORDERSTYLE_ROUNDED
},
"#password": {
top: 20,
width: "80%",
borderStyle: Ti.UI.INPUT_BORDERSTYLE_ROUNDED
},
"#loginBtn": {
top: 20,
width: "50%"
}
app/controllers/login.js:
function doLogin(e) {
var username = $.username.value;
var password = $.password.value;
if (!username || !password) {
alert("Please enter both fields");
return;
}
// Proceed with authentication
}
$.loginWindow.open();
Classic implementation
Replace app.js with imperative calls:
var win = Ti.UI.createWindow({
backgroundColor: "#fff",
layout: "vertical"
});
var username = Ti.UI.createTextField({
hintText: "Username",
top: 20,
width: "80%",
borderStyle: Ti.UI.INPUT_BORDERSTYLE_ROUNDED
});
var password = Ti.UI.createTextField({
hintText: "Password",
passwordMask: true,
top: 20,
width: "80%",
borderStyle: Ti.UI.INPUT_BORDERSTYLE_ROUNDED
});
var loginBtn = Ti.UI.createButton({
title: "Log In",
top: 20,
width: "50%"
});
loginBtn.addEventListener("click", function(e) {
if (!username.value || !password.value) {
alert("Please enter both fields");
return;
}
// Proceed with authentication
});
win.add(username);
win.add(password);
win.add(loginBtn);
win.open();
Notice that every layout property is repeated inline. In a real app with ten screens, that repetition compounds.
How to validate the choice on your machine
Run these commands in your terminal from the directory where you want the project created. You need the Titanium CLI installed and, for Android builds, the Android SDK and a running emulator or device.
ti create --id com.example.login --name LoginApp
Scaffolds a new project. If the Titanium SDK is not yet downloaded, the CLI fetches it automatically.cd LoginApp
Enter the project root.- For Alloy, place the three files above under
app/views/,app/styles/, andapp/controllers/. For Classic, replaceapp.js. ti build -p android -T emulator
Compiles and installs the app on the Android emulator.
Expected checks: the emulator displays a vertical layout with two text fields and a button. Tapping the button while either field is empty triggers an alert with the message "Please enter both fields."
Risks: if the Alloy CLI version does not match your Titanium SDK version, the Alloy build can fail with compilation errors before packaging. Run ti info to see the active SDK and CLI versions, and align them if the build aborts. The classic path has no extra compile layer, but it still depends on a correctly configured Android environment.
If you want to compare code volume directly, count the non-comment lines in each implementation. The classic version repeats border style, width, and top spacing for every control, while the Alloy version centralizes those values in the TSS file. That difference is the core maintainability argument.
Limitations and when to reconsider
- Alloy adds a build dependency. Continuous integration pipelines must install a compatible Alloy CLI alongside the Titanium SDK. A version mismatch is the most common failure point.
- Classic does not scale cleanly. Beyond a handful of screens, the lack of separation makes it harder to apply global style changes or hand UI files to a designer.
- Alloy hides some Titanium API. Automatic data binding is helpful for collections, but developers who never learn the imperative layer may struggle when they need a custom widget or a workaround that Alloy does not expose directly.
- Neither changes the final binary. Both produce the same native Titanium runtime, so the decision should be based on source-code ergonomics and team skill, not on runtime benchmarks.
If your team already works with MVC patterns and expects the app to grow past a few screens, Alloy is the safer default. If you are building a throwaway prototype or a single-screen utility and want to avoid the extra compile step, classic Titanium is perfectly reasonable.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.