Blog

Luis Majano

June 10, 2026

Spread the word


Share your thoughts

BoxLang 1.14 ships with one of the most developer-friendly OOP features we've built yet: local template classes. If you've ever created a throwaway .bx file just to hold a five-line helper class, this one's for you.

The Problem With File-Per-Class

BoxLang, like most class-based languages, traditionally ties class definitions to files. One class, one file. That's great for production code you'll reuse everywhere -- but it's friction when you need a small data container, a quick utility, or a Java interface implementation that lives and dies with a single script. You end up with a folder of .bx files that exist solely to support one other file.

Local template classes eliminate that friction entirely.


What Are Local Template Classes?

A local template class (also called a template class) is a named class defined directly inside a .bxs script or inside a <bx:script> island in a .bxm template. It's compiled as part of the enclosing file, scoped to that compilation unit, and fully featured -- properties, constructors, inheritance, static members, Java interop, the works.

class Greeter {
    function greet( name ) {
        return "Hello, " & name & "!"
    }
}

result = new Greeter().greet( "World" )
// "Hello, World!"

That's it. No separate file. No import statement. The class is available right there in the same script. Try it for yourself on try.boxlang.io.

Once you create an instance, you can pass it anywhere in your application -- the local-only restriction applies to the class definition, not to the instance.

BoxLang-only feature: Template classes are not available in the CFML parser. They are a BoxLang-exclusive language feature.


Hoisting -- Use Before You Define

Template classes are hoisted to the top of the compilation unit. That means you can instantiate a class above its definition in the file and the compiler won't complain.

// Instantiating before definition -- works perfectly
result = new Greeter().greet( "World" )

class Greeter {
    function greet( name ) {
        return "Hello, " & name & "!"
    }
}

println( result )

This keeps your script's main logic at the top and the supporting class definitions below, which often reads more naturally.


Multiple Classes in One File

You can define as many template classes as you need in a single script:

class Adder {
    function add( a, b ) {
        return a + b
    }
}

class Multiplier {
    function multiply( a, b ) {
        return a * b
    }
}

myAdder      = new Adder()
myMultiplier = new Multiplier()
result       = myMultiplier.multiply( myAdder.add( 2, 3 ), 4 )
// 20

Each class name must be unique within the file (case-insensitive). Duplicate names throw a compile-time error.


Properties, Constructors, and Accessors

Template classes support the full property system -- including default values and auto-generated getters/setters (remember, accessors=true is the BoxLang default):

class Counter {
    property name="count" default=0;

    function init( numeric startingValue=0 ) {
        variables.count = arguments.startingValue
    }

    function increment() {
        variables.count++
    }
}

c = new Counter( 5 )
c.increment()
c.increment()
c.increment()
println( c.getCount() ) // 8 -- getCount() generated automatically

Static Members

Static initializer blocks, static variables, and static methods are all supported:

class Config {
    static {
        static.MAX_RETRIES = 5
        static.APP_NAME    = "MyApp"
    }
}

println( Config::MAX_RETRIES ) // 5
println( Config::APP_NAME )    // "MyApp"

class MathUtil {
    static function add( a, b ) {
        return a + b
    }

    static function multiply( a, b ) {
        return a * b
    }
}

result  = MathUtil::add( 3, 4 )       // 7
result2 = MathUtil::multiply( 5, 6 )  // 30

println( result )
println( result2 )

Inheritance, Abstract, and Final

Template classes can extend other template classes defined in the same file, with full multi-level inheritance and super support:

class Vehicle {
    function init( make ) {
        variables.make = make
        return this
    }

    function getMake() {
        return variables.make
    }
}

class Car extends="Vehicle" {
    function init( make, model ) {
        super.init( make )
        variables.model = model
        return this
    }

    function getInfo() {
        return this.getMake() & " " & variables.model
    }
}

result = new Car( "Toyota", "Camry" ).getInfo()
// "Toyota Camry"

Abstract and final modifiers work exactly as you'd expect:

// Abstract -- cannot be instantiated directly
abstract class Shape {
    function describe() {
        return "I am a shape"
    }
}

class Circle extends="Shape" {
    function init( radius ) {
        variables.radius = radius
        return this
    }

    function getRadius() {
        return variables.radius
    }
}

myCircle = new Circle( 5 )
println( myCircle.describe() )   // "I am a shape"
println( myCircle.getRadius() )  // 5
// Final -- cannot be extended
final class Immutable {
    function getValue() {
        return "fixed"
    }
}

There is no limit to inheritance depth -- just like top-level classes.


Java Interop -- Implementing Interfaces and Extending Java Classes

This is where template classes really shine for JVM developers. You can implement Java interfaces and extend Java base classes inline, without any scaffolding:

Implementing Runnable

import java:java.lang.Thread

class MyRunnable implements="java:java.lang.Runnable" {
    property name="didRun" default=false;

    function run() {
        variables.didRun = true
    }
}

r       = new MyRunnable()
jThread = new java:Thread( r )
jThread.start()
jThread.join()
println( r.getDidRun() ) // true

Extending a Java Base Class

class MyTask extends="java:java.util.TimerTask" {

    @override
    void function run() {
        println( "Hello from local TimerTask!" )
    }

}

task = new MyTask()
println( task instanceof "java.util.TimerTask" )

Implementing Comparable

class Ranked implements="java:java.lang.Comparable" {
    
  property name="rank" default=0;

    function init( rank ) {
        variables.rank = rank
        return this
    }

    int function compareTo( other ) {
        return variables.rank - other.getRank()
    }
}

a = new Ranked( 3 )
b = new Ranked( 7 )
println( a.compareTo( b ) ) // negative (3 < 7)

Shared Imports

Template classes inherit the import declarations from their enclosing script. Any import at the top of your .bxs file is available inside every template class defined in that file:

import java.util.Date

class Event {
    function init( name ) {
        variables.name      = name
        variables.timestamp = new Date()
        return this
    }

    function getInfo() {
        return variables.name & " at " & variables.timestamp.toString()
    }
}

result = new Event( "BoxLang Launch" ).getInfo()
// "BoxLang Launch at Tue Jun 03 12:34: 56 UTC 2026"

Metadata

Template classes expose full metadata through the standard BoxLang APIs:

class Person {
    property name="firstName" default="John";
    property name="lastName" default="Doe";

    function fullName() {
        return this.getFirstName() & " " & this.getLastName()
    }
}

meta = getMetadata( new Person() )
println( meta.name )       // "Person"
println( meta.type )       // "Class"
println( meta.properties ) // Array with 2 entries

You can also look up metadata by name without instantiating first:

meta = getClassMetadata( "Person" )
println( meta.name ) // "Person"

Note: getClassMetadata() is the BoxLang rename of CFML's getComponentMetadata() -- same behavior, aligned naming convention.


Using Template Classes in .bxm Templates

Template classes can be defined inside <bx:script> islands within your .bxm markup templates:

<bx:script>
    class Point {
        function init( x, y ) {
            variables.x = x
            variables.y = y
            return this
        }

        function toString() {
            return "(" & variables.x & "," & variables.y & ")"
        }
    }

    result = new Point( 3, 4 ).toString()
</bx:script>

<bx:output>#result#</bx:output>

You can even nest inner classes inside a template class within a script island:

<bx:script>
    class Parent {
        class Child {
            function greet() {
                return "hi"
            }
        }

        function getChild() {
            return new Child()
        }
    }

    result = new Parent().getChild().greet()
</bx:script>
<!-- Result: "hi" -->

Limitations

A few constraints to keep in mind:

No duplicate names. Class names must be unique within a file, case-insensitively. Person and PERSON in the same file is a compile error.

No import conflicts. A template class name cannot share a name with an imported alias -- and vice versa.

No class definitions inside functions. Template classes must live at the top level of the script or template. Defining one inside a function body throws a compile error.

Script-only. Classes cannot be defined in tag-based BoxLang source outside of a <bx:script> block.


Try It Now

Template classes work on try.boxlang.io today -- that's actually one of the motivating use cases. Since the online REPL compiles a single file at a time, template classes let you build real OOP examples without any file system at all. Paste any of the snippets above and run them instantly.

The feature is available now on the 1.14 snapshot builds. Give it a spin and let us know what you build.


What's Next

Template classes are one half of the inline class story in BoxLang 1.14. The other half is inner classes -- named classes defined inside .bx class bodies, accessible via the $ separator syntax for external use. Read about them here: Introducing Inner Classes in BoxLang.

Full documentation is available at boxlang.ortusbooks.com/boxlang-language/classes/template-classes.


Resources


Have questions or feedback? Join the conversation on the Ortus Community. Follow us on X and LinkedIn for the latest BoxLang news.

Add Your Comment

Recent Entries

Ortus Solutions and BoxLang at CFCamp 2026: Platinum Sponsor, Keynote Leaders, and a Full Lineup of Innovation

Ortus Solutions and BoxLang at CFCamp 2026: Platinum Sponsor, Keynote Leaders, and a Full Lineup of Innovation

CFCamp 2026 was an important milestone for the Ortus Solutions team and for the growing BoxLang ecosystem.

This year, Ortus Solutions participated as a Platinum Sponsor and had the honor of leading the official Keynote, where Luis Majano, Brad Wood, and Jacob Beers shared major updates around ColdBox, BoxLang, AI, and multi-runtime support.

The message was clear: the CFML ecosystem is not standing still. With BoxLang, ColdBox, C...

Cristobal Escobar
Cristobal Escobar
July 02, 2026
Into the Box 2026 Presentation Slides Are Now Available

Into the Box 2026 Presentation Slides Are Now Available

One of the best parts of Into the Box is that the learning doesn't end when the conference does.

We're excited to share that all official Into the Box 2026 presentation slides are now publicly available. Whether you attended the conference and want to revisit your favorite sessions or you're exploring the content for the first time, you can now browse the complete collection of presentation decks.

Victor Campos
Victor Campos
June 30, 2026