View Source Kino (Kino v0.10.0)

Client-driven interactive widgets for Livebook.

Kino is the library used by Livebook to render rich and interactive outputs directly from your Elixir code.

getting-started

Getting started

Livebook is distributed with a set of interactive tutorials and examples, including some that specifically focus on Kino. If you're just getting started, going through these is highly recommended.

You can access these notebooks by starting Livebook and clicking on "Learn" in the sidebar.

built-in-kinos

Built-in kinos

Kino renders any data structure that implements the Kino.Render protocol, falling back to the Kernel.inspect/2 representation whenever an implementation is not available. The data structures supported by Kino out of the box are:

kino-datatable

Kino.DataTable

Kino.DataTable implements a data table output for user-provided tabular data:

data = [
  %{id: 1, name: "Elixir", website: "https://elixir-lang.org"},
  %{id: 2, name: "Erlang", website: "https://www.erlang.org"}
]

Kino.DataTable.new(data)

kino-ets

Kino.ETS

Kino.ETS implements a data table output for ETS tables in the system:

tid = :ets.new(:users, [:set, :public])
Kino.ETS.new(tid)

kino-image

Kino.Image

Kino.Image wraps binary image content and can be used to render raw images of any given format:

content = File.read!("/path/to/image.jpeg")
Kino.Image.new(content, "image/jpeg")

kino-markdown

Kino.Markdown

Kino.Markdown renders Markdown content, in case you need richer text:

Kino.Markdown.new("""
# Example

A regular Markdown file.

## Code

```elixir
"Elixir" |> String.graphemes() |> Enum.frequencies()
```

## Table

| ID | Name   | Website                 |
| -- | ------ | ----------------------- |
| 1  | Elixir | https://elixir-lang.org |
| 2  | Erlang | https://www.erlang.org  |
""")

kino-text

Kino.Text

Kino.Text renders plain text content. It is similar to Kino.Markdown, however doesn't interpret any markup.

Kino.Text.new("Hello!")

kino-mermaid

Kino.Mermaid

Kino.Mermaid renders Mermaid graphs:

Kino.Mermaid.new("""
graph TD;
  A-->B;
  A-->C;
  B-->D;
  C-->D;
""")

kino-frame

Kino.Frame

Kino.Frame is a placeholder for static outputs that can be dynamically updated.

frame = Kino.Frame.new() |> Kino.render()

for i <- 1..100 do
  Kino.Frame.render(frame, i)
  Process.sleep(50)
end

Also see Kino.animate/3.

kino-tree

Kino.Tree

Kino.Tree displays arbitrarily nested data structure as a tree view.

data = Process.info(self())
Kino.Tree.new(data)

kino-html

Kino.HTML

Kino.HTML displays arbitrary static HTML.

Kino.HTML.new("""
<h3>Look!</h3>

<p>I wrote this HTML from <strong>Kino</strong>!</p>
""")

user-interactions

User interactions

Kino.Input and Kino.Control provide a set of widgets for entering data and capturing user events. See the respective module documentation for examples.

all-others

All others

All other data structures are rendered as text using Elixir's Kernel.inspect/2.

custom-kinos

Custom kinos

Kino makes it possible to define custom JavaScript powered kinos, see Kino.JS and Kino.JS.Live for more details.

Packaging

When publishing custom kinos and smart cells, please consider the following guidelines:

  • prefix package name with kino_, usually followed by the name of the integration, such as kino_vega_lite, kino_ecto

  • namespace all modules under KinoExample, not Kino.Example. Note that official packages maintained by the Livebook team expose public APIs under Kino., because they are essentially direct extensions of Kino and we make sure no conflicting modules exist. Unofficial packages should follow the usual Elixir conventions with respect to module names

Link to this section Summary

Functions

Renders a kino that periodically calls the given function to render a new result.

Same as listen/2, except each event is processed concurrently.

Configures Kino.

Inspects the given term as cell output.

Interrupts evaluation with the given message.

Consumes a stream with fun without blocking execution.

Returns a special value that results in no visible output.

Renders the given term as cell output.

Starts a process under the Kino supervisor.

Terminates a child started with start_child/1.

Link to this section Types

@type nothing() :: :"do not show this result in output"

Link to this section Functions

Link to this function

animate(stream_or_interval_ms, fun)

View Source
@spec animate(Enumerable.t() | pos_integer(), (term() -> any())) :: nothing()

Renders a kino that periodically calls the given function to render a new result.

The callback receives a stream element and should return a term to be rendered.

This function uses Kino.Frame as the underlying kino. It returns nothing (a non-printable result).

examples

Examples

An animation is created by turning a stream of values into subsequent animation frames:

Stream.interval(100)
|> Stream.take(100)
|> Kino.animate(fn i ->
  Kino.Markdown.new("**Iteration: `#{i}`**")
end)

Alternatively an integer may be passed as a shorthand for Stream.interval/1:

# Render new Markdown every 100ms
Kino.animate(100, fn i ->
  Kino.Markdown.new("**Iteration: `#{i}`**")
end)
Link to this function

animate(stream_or_interval_ms, state, fun)

View Source
@spec animate(
  Enumerable.t() | pos_integer(),
  state,
  (term(), state -> {:cont, term(), state} | :halt)
) :: nothing()
when state: term()

A stateful version of animate/2.

The callback receives a stream element and the accumulated state and it should return either of:

  • {:cont, term_to_render, state} - the continue

  • :halt - to no longer schedule callback evaluation

examples

Examples

This function is primarily useful to consume Kino.Control events:

button = Kino.Control.button("Click")

button
|> Kino.Control.stream()
|> Kino.animate(0, fn _event, counter ->
  new_counter = counter + 1
  md = Kino.Markdown.new("**Clicks: `#{new_counter}`**")
  {:cont, md, new_counter}
end)
Link to this function

async_listen(stream_or_interval_ms, fun)

View Source
@spec async_listen(Enumerable.t() | pos_integer(), (term() -> any())) :: :ok

Same as listen/2, except each event is processed concurrently.

@spec configure(keyword()) :: :ok

Configures Kino.

The supported options are:

  • :inspect

They are discussed individually in the sections below.

inspect

Inspect

A keyword list containing inspect options used for printing usual evaluation results. Defaults to pretty formatting with a limit of 50 entries.

To show more entries, you configure a higher limit:

Kino.configure(inspect: [limit: 200])

You can also show all entries by setting the limit to :infinity, but keep in mind that for large data structures it is memory-expensive and is not an advised configuration in this case. Instead prefer the use of IO.inspect/2 with :infinity limit when needed.

See Inspect.Opts for the full list of options.

Link to this function

inspect(term, opts \\ [])

View Source
@spec inspect(
  term(),
  keyword()
) :: term()

Inspects the given term as cell output.

This works essentially the same as IO.inspect/2, except it always produces colored text and respects the configuration set with configure/1.

Opposite to render/1, it does not attempt to render the given term as a kino.

Link to this function

interrupt!(variant, message)

View Source
@spec interrupt!(:normal | :error, String.t()) :: no_return()

Interrupts evaluation with the given message.

This function raises a specific error to let Livebook known that evaluation should be stopped. The error message is shown to the user and they can retry evaluation with a button click, supposedly after they resolve the interrupt reason.

examples

Examples

text =
  Kino.Input.text("Input")
  |> Kino.render()
  |> Kino.Input.read()

if text == "" do
  Kino.interrupt!(:error, "Input required")
end

# This will not be run if the `interrupt!` is called above
Kino.Markdown.new("**You entered:** #{text}")
Link to this function

listen(stream_or_interval_ms, fun)

View Source
@spec listen(Enumerable.t() | pos_integer(), (term() -> any())) :: :ok

Consumes a stream with fun without blocking execution.

Note that events are processed by fun sequentially. If you want to process them concurrently, use async_listen/2.

examples

Examples

This function is primarily useful to consume Kino.Control events:

Kino.Control.button("Greet")
|> Kino.listen(fn event -> IO.inspect(event) end)

You can also merge multiple controls into a single stream. For example, in order to merge them and tag each with a distinct event:

button = Kino.Control.button("Hello")
input = Kino.Input.checkbox("Check")

stream = Kino.Control.tagged_stream([hello: button, check: input])

Kino.listen(stream, fn
  {:hello, event} -> ...
  {:check, event} -> ...
end)

Any other stream works as well:

Stream.interval(100)
|> Stream.take(10)
|> Kino.listen(fn i -> IO.puts("Ping #{i}") end)

Finally, an integer may be passed as a shorthand for Stream.interval/1:

Kino.listen(100, fn i -> IO.puts("Ping #{i}") end)
Link to this function

listen(stream_or_interval_ms, state, fun)

View Source
@spec listen(
  Enumerable.t() | pos_integer(),
  state,
  (term(), state -> {:cont, state} | :halt)
) :: :ok
when state: term()

A stateful version of listen/2.

The callback should return either of:

  • {:cont, state} - the continue

  • :halt - to stop listening

examples

Examples

button = Kino.Control.button("Click")

Kino.listen(button, 0, fn _event, counter ->
  new_counter = counter + 1
  IO.puts("Clicks: #{new_counter}")
  {:cont, new_counter}
end)
@spec nothing() :: nothing()

Returns a special value that results in no visible output.

examples

Examples

This is especially handy when you wish to suppress the default output of a cell. For instance, a cell containing this would normally result in verbose response output:

resp = Req.get!("https://example.org")

That output can be suppressed by appending a call to nothing/0:

resp = Req.get!("https://example.org")
Kino.nothing()
@spec render(term()) :: term()

Renders the given term as cell output.

This effectively allows any Livebook cell to have multiple evaluation results.

Starts a process under the Kino supervisor.

The process is automatically terminated when the current process terminates or the current cell reevaluates.

If you want to terminate the started process, use terminate_child/1. If you terminate the process manually, the Kino supervisor might restart it if the child's :restart strategy says so.

Nested start

It is not possible to use start_child/1 while initializing another process started this way. In other words, you generally cannot call start_child/1 inside callbacks such as GenServer.init/1 or Kino.JS.Live.init/2. If you do that, starting the process will block forever.

Note that creating many kinos uses start_child/1 underneath, hence the same restriction applies to starting those. See Kino.JS.Live.init/2 for more details.

Link to this function

terminate_child(pid)

View Source (since 0.9.1)
@spec terminate_child(pid()) :: :ok | {:error, :not_found}

Terminates a child started with start_child/1.

Returns :ok if the child was found and terminated, or {:error, :not_found} if the child was not found.