Migration Guide: DIY to Raxol
View SourceAlready built your own terminal rendering? This guide shows how to integrate or migrate to Raxol.
New to Raxol? See the Quickstart first.
Why Migrate?
You've built a working terminal renderer. Here's what Raxol adds on top:
LiveView integration. If you're rendering terminals in web apps, Raxol.LiveView handles buffer-to-HTML conversion, CSS theming (5 built-in themes), event handling, and 60fps rendering optimizations.
Testing utilities. Instead of fragile string matching against rendered output, you can test the actual data structure:
buffer = Buffer.write_at(buffer, 5, 3, "expected text")
cell = Buffer.get_cell(buffer, 5, 3)
assert cell.char == "e"Performance optimizations. Diff rendering (50x faster updates), benchmarking suite, memory profiling, automated regression detection.
You can adopt Raxol incrementally -- your existing code keeps working.
Migration Strategies
Strategy 1: Side-by-Side (lowest risk)
Run both implementations, compare outputs in dev/test:
defmodule MyApp.Renderer do
def render(data) do
your_buffer = YourRenderer.create_buffer(data)
your_output = YourRenderer.render(your_buffer)
raxol_buffer = RaxolAdapter.from_your_format(your_buffer)
raxol_output = Raxol.Core.Buffer.to_string(raxol_buffer)
if Mix.env() != :prod do
compare_outputs(your_output, raxol_output)
end
your_output
end
endZero risk to production. Good for validating migration and finding edge cases. Doubled rendering cost in dev/test.
Strategy 2: Feature Flagging
Gradually roll out Raxol:
defmodule MyApp.Renderer do
def render(data, opts \\ []) do
if Keyword.get(opts, :use_raxol, false) ||
Application.get_env(:my_app, :raxol_enabled, false) do
render_with_raxol(data)
else
render_with_your_code(data)
end
end
endEasy rollback. A/B testing possible. You maintain both paths until the switch is complete.
Strategy 3: Module Replacement
Replace your module with a Raxol adapter:
# Before:
defmodule MyApp.Buffer do
def create(width, height), do: # your code
def write_at(buffer, x, y, text), do: # your code
end
# After:
defmodule MyApp.Buffer do
defdelegate create(width, height), to: Raxol.Core.Buffer, as: :create_blank_buffer
defdelegate write_at(buffer, x, y, text), to: Raxol.Core.Buffer
defdelegate write_at(buffer, x, y, text, style), to: Raxol.Core.Buffer
# Keep custom functions
def your_special_function(buffer), do: # your code
endMinimal code changes. Keep your existing API.
Strategy 4: Clean Break
Rewrite from scratch using Raxol. Simplest long-term, but highest risk. Best for small codebases or greenfield projects.
Adapting Your Buffer Format
Most DIY implementations use similar structures. Here's a typical adapter:
defmodule MyApp.BufferAdapter do
def to_raxol(your_buffer) do
raxol_buffer = Raxol.Core.Buffer.create_blank_buffer(
your_buffer.width,
your_buffer.height
)
Enum.reduce(your_buffer.cells, raxol_buffer, fn cell, buf ->
style = %{
fg_color: cell.fg,
bg_color: Map.get(cell, :bg),
bold: Map.get(cell, :bold, false)
}
Raxol.Core.Buffer.set_cell(buf, cell.x, cell.y, cell.char, style)
end)
end
def from_raxol(raxol_buffer) do
cells =
for {line, y} <- Enum.with_index(raxol_buffer.lines),
{cell, x} <- Enum.with_index(line.cells) do
%{x: x, y: y, char: cell.char, fg: cell.style[:fg_color]}
end
%{width: raxol_buffer.width, height: raxol_buffer.height, cells: cells}
end
endAdapters add overhead. Benchmark both paths -- if the adapter is > 2x slower, consider Strategy 3 or 4.
Incremental Migration
Phase 1: Add {:raxol, "~> 2.0"} to deps (or path: "../raxol" before Hex publish). Run tests, ensure no conflicts.
Phase 2: Create adapters. Write round-trip tests to verify conversions preserve data.
Phase 3: If you're using Phoenix, use Raxol.LiveView.TerminalComponent for web rendering. Keep your buffer code, convert via adapter.
Phase 4: Gradually replace custom components -- box drawing, text rendering, diffing -- with Raxol equivalents.
Phase 5: Once confidence is high, remove old code and adapters.
Phase 6: Add performance tracking. Log slow renders.
Feature Parity Checklist
Before migrating, verify Raxol covers your needs:
- [ ] Create/write/read/clear/resize buffers
- [ ] Foreground/background colors (16, 256, RGB)
- [ ] Bold, italic, underline, strikethrough
- [ ] Box drawing (single, double, rounded, custom)
- [ ] Full and diff rendering
- [ ] ANSI and HTML output
- [ ] Unicode support (graphemes, wide chars, emoji)
- [ ] < 1ms buffer ops, < 16ms full renders
If something's missing, open a GitHub issue.
FAQ
Will this break my existing code? No. Raxol runs alongside your code. Use adapters for gradual migration.
What if Raxol is missing a feature I need? Keep that part of your code, extend Raxol with a plugin, or open a GitHub issue.
Can I contribute my adapter back? Yes. Open a PR with your adapter in lib/raxol/adapters/.