As simple as possible, but not simpler.

Codion Desktop
Application Framework

Observable Models

public final class CandidateModel {
    
    private final FilterTableModel<CandidateRow, CandidateColumn> tableModel;

    private final State installedOnly;

    public CandidateModel() {
        tableModel = FilterTableModel.builder()
            .columns(new CandidateTableColumns())
            .items(new CandidateItems())
            .included(new CandidateIncluded())
            .build()

        installedOnly = State.builder()
            .listener(tableModel.items()::filter)
            .build()
    }

    public FilterTableModel<CandidateRow, CandidateColumn> tableModel() {
        return tableModel;
    }

    public State installedOnly() {
        return installedOnly;
    }
}

Reactive Components

public final class CandidatePanel extends JPanel {
    
    private final FilterTable<CandidateRow, CandidateColumn> table;
    
    private final JCheckBox installedOnly;

    public CandidatePanel(CandidateModel candidateModel, ObservableState installing) {        
        table = FilterTable.builder()
            .model(candidateModel.tableModel())
            .sortable(false)
            .enabled(installing.not())
            .cellRenderer(CandidateColumn.INSTALLED,
                FilterTableCellRenderer.builder()
                    .columnClass(Integer.class)
                    .horizontalAlignment(CENTER)
                    .build())
            .build();

        installedOnly = checkBox()
            .link(candidateModel.installedOnly())
            .text("Installed")
            .mnemonic('T')
            .enabled(installing.not())
            .build();
    }
}

Type-Safe Domain Modeling

// Domain API
interface Country {
    EntityType TYPE = DOMAIN.entityType("world.country");
    
    Column<String> CODE = TYPE.stringColumn("code");
    Column<String> NAME = TYPE.stringColumn("name");
    Column<Integer> POPULATION = TYPE.integerColumn("population");
    Column<Integer> CAPITAL_ID = TYPE.integerColumn("capital_id");
    
    ForeignKey CAPITAL_FK = TYPE.foreignKey("capital_fk", CAPITAL_ID, City.ID);
}

// Domain Implementation  
EntityDefinition country() {
    return Country.TYPE.define(
        Country.CODE.define()
            .primaryKey()
            .caption("Code")
            .maximumLength(3),
        Country.NAME.define()
            .column()
            .caption("Name")
            .nullable(false)
            .maximumLength(52),
        Country.POPULATION.define()
            .column()
            .caption("Population")
            .nullable(false),
        Country.CAPITAL_ID.define()
            .column()
            .nullable(false),
        Country.CAPITAL_FK.define()
            .foreignKey()
            .caption("Capital"))
    .caption("Country")
    .build();
}

Declarative UI Building

// UI Component Creation & Layout
@Override
protected void initializeUI() {
    // Create typed components
    createTextField(Country.CODE)
        .columns(3)
        .upperCase(true);
    createTextField(Country.NAME)
        .columns(20);
    createIntegerField(Country.POPULATION)
        .columns(10)
        .minimum(0)
        .nullable(false);
    createComboBoxPanel(Country.CAPITAL_FK, this::createCapitalEditPanel)
        .includeAddButton(true)
        .includeEditButton(true)
        .preferredComboBoxWidth(180);

    setLayout(flexibleGridLayout(3, 1));

    // Layout with input panels
    add(borderLayoutPanel()
        .west(createInputPanel(Country.CODE))
        .center(createInputPanel(Country.NAME)));
    addInputPanel(Country.POPULATION);
    addInputPanel(Country.CAPITAL_FK);
}

// Complete Application Bootstrap
EntityApplication.builder(WorldAppModel.class, WorldAppPanel.class)
    .domain(World.DOMAIN)
    .version(WorldAppModel.VERSION)
    .icon(WorldAppPanel.ICON)
    .startupDialog(false)
    .defaultLookAndFeel(MonokaiPro.class)
    .defaultUser(User.parse("scott:tiger"))
    .start();

Key Features

πŸ–₯️ Desktop First

When web deployment isn't required, desktop applications can provide responsive interfaces without browser constraints.

Native Desktop Advantages
β€’ Keyboard navigation throughout with plenty of shortcuts
β€’ System integration with file dialogs, clipboard, and desktop features
β€’ True multitasking without browser tab limitations
β€’ Lower memory usage compared to web applications
β€’ Offline capability with local data and no network dependencies

Desktop UI Components
Tables, forms, and controls that handle large datasets efficiently. Context menus, drag-and-drop, focus management, and accessibility features that work across platforms.

Modern Themes
Include ~50 FlatLAF Look and Feels. Users can dynamically switch between Material themes (Darker, Oceanic, Night Owl), developer favorites (Dracula, One Dark, Gruvbox), or nature-inspired themes (Aurora Borealis, Sakura) without restarting (View β†’ Select Look & Feel).

// Include the 6 core Flat Look and Feels
<dependency>
    <groupId>is.codion</groupId>
    <artifactId>codion-plugin-flatlaf</artifactId>
</dependency>

// Include ~50 IntelliJ theme based Flat Look and Feels
<dependency>
    <groupId>is.codion</groupId>
    <artifactId>codion-plugin-flatlaf-intellij-themes</artifactId>
</dependency>

Learn more about desktop applications β†’

πŸ”’ Type Safe

Every aspect of Codion is compile-time checked. No string-based column references, no runtime surprises. Foreign keys are first-class citizens with automatic loading.

// Compile-time safety everywhere
Entity country = connection.selectSingle(Country.CODE.equalTo("USA"));
String capitalName = country.get(Country.CAPITAL_FK).get(City.NAME);

// Query results are type-checked at compile time
List<String> artistNames = connection.select(Artist.NAME, Artist.ID.in(1, 2));

// Generic column definitions with type safety
Column<List<String>> TAGS = TYPE.column("tags", new TypeReference<>() {});
Column<Location> COORDINATES = TYPE.column("location", Location.class);

// Conditions enforce correct attribute types
Condition lowRated = Track.RATING.lessThanOrEqualTo(4);
Condition expensive = Track.UNITPRICE.greaterThan(BigDecimal.ONE);

πŸ”„ Observable by Design

Everything in Codion is observable. UI components automatically update when data changes. No manual synchronization, no event bus complexity. Just declare relationships and watch them work.

// Traditional approach - manual synchronization
textField.getDocument().addDocumentListener(new DocumentListener() {
    public void changedUpdate(DocumentEvent e) { updateFilter(); }
    public void removeUpdate(DocumentEvent e) { updateFilter(); }  
    public void insertUpdate(DocumentEvent e) { updateFilter(); }
});
button.setEnabled(false);  // Remember to update this everywhere!

// Codion approach - synchronous observable binding
Value<String> filter = Value.nullable();
State canSubmit = State.and(hasData, isValid);

JTextField filterField = Components.stringField()
    .link(filter)
    .hint("Filter...")
    .build();
    
JButton submitButton = Components.button()
    .action(submitAction)
    .enabled(canSubmit)  // Updates automatically and synchronously!
    .build();

Explore observable patterns (synchronous & debuggable) β†’

πŸ“‰ Reduces Complexity

Adding ONE detail table causes 61-109% code explosions in other frameworks. That's just basic CRUD - for business-grade features (sortable columns, keyboard navigation, export, validation indicators), other frameworks need 10-15x more code.

// Basic master-detail
React + Node.js:  515 lines
Spring Boot:      355 lines 
Codion:           143 lines (with enterprise features included)

// For feature parity with Codion's defaults:
React + Node.js:  ~1,915 lines (+1,400 for table features)
Spring Boot:      ~1,355 lines (+1,000 for UI features)
Codion:           143 lines (no additional code needed)

View the complexity comparison β†’

βš”οΈ Battle tested

Codion has powered 50+ production applications over 20 years across scientific and government institutions, including critical infrastructure systems.

Learn about Codion's development approach β†’

πŸͺΆ Lightweight

Codion is built on Java Standard Edition - Swing, JDBC, and RMI. No third-party application servers or complex deployment scenarios.

Resource Efficiency
Codion applications typically use fewer resources than Electron-based counterparts:

// Typical resource usage comparison:
Electron app:     400MB+ RAM, 150MB+ disk
Codion app:       40-80MB RAM, 15MB disk

// CPU efficiency:
Electron: Constant background activity
Codion: Near-zero when idle

πŸ€– AI/LLM Ready

Codion's parameterized builder APIs enable code generation. Every method takes explicit parameters, making it suitable for AI-assisted development.

// LLM-friendly APIs
Components.stringField()
    .link(nameValue)
    .columns(20)
    .upperCase(true)
    .maximumLength(20)
    .action(insertAction)
    .horizontalAlignment(CENTER)
    .selectAllOnFocusGained(true)
    .transferFocusOnEnter(true);

Learn about code generation capabilities β†’

πŸ—οΈ Domain-Driven Design

Your business domain becomes executable, type-safe code. Define entities once with rich metadata, and Codion generates all CRUD operations, validations, and UI components automatically.

// Define your domain - this is all you need!
interface Customer {
    EntityType TYPE = DOMAIN.entityType("store.customer");
    
    Column<String> NAME = TYPE.stringColumn("name");
    Column<String> EMAIL = TYPE.stringColumn("email");
    Column<BigDecimal> CREDIT_LIMIT = TYPE.bigDecimalColumn("credit_limit");
}

// This definition provides:
// βœ“ Type-safe CRUD operations
// βœ“ Form validation and UI components  
// βœ“ Search and filter capabilities
// βœ“ Foreign key navigation

// Use it everywhere with compile-time safety:
Entity.Key customerKey = connection.insert(
    entities.entity(Customer.TYPE)
        .with(Customer.NAME, "John Doe")
        .with(Customer.EMAIL, "john@example.com")
        .with(Customer.CREDIT_LIMIT, new BigDecimal(10_000))
        .build());

Explore domain modeling β†’

πŸš€ Rapid Development With Joy

Minimal boilerplate and configuration. From domain model to working CRUD application in hours. Clean APIs with IDE support and compile-time safety.

// Domain definitions for a couple of tables ~100 lines
// Initial application and UI setup ~150 lines
// Run application and start testing

πŸ–₯️ See Codion in Action