Since I am still working on next major release of Rye-fyne / Rye and unifying related info on the website, I ask you to no post this page to ’tech news sites’ at this point.
Rye-fyne has undergone a major upgrade with a new import system and updated syntax. This page contains examples updated for the new system. The new approach uses explicit Go module imports and namespaced widget/container calls for better clarity and maintainability.
If the code below doesn’t always make sense to you, check out Meet Rye, especially the part about op and pipe-words.
What’s new: import\go app/new widget/label .window .set-content .show-and-run
We’ll start with a Hello world app. The new system requires explicit imports and uses namespaced function calls for widgets and containers.

fyne: import\go "fyne"
app: import\go "fyne/app"
widget: import\go "fyne/widget"
a: app/new
w: a .window "Hello"
w .set-content widget/label "Hello fyne world!"
w .show-and-run
What’s new: container/hbox container/vbox
Fyne uses various layout components that you can combine to declaratively lay out the widgets. The two very basic ones are now called container/hbox and container/vbox.

fyne: import\go "fyne"
app: import\go "fyne/app"
widget: import\go "fyne/widget"
container: import\go "fyne/container"
a: app/new
w: a .window "Layouts"
w .set-content container/vbox [
container/hbox [
widget/label "Don't look RIGHT"
widget/label "Don't look LEFT"
]
widget/label "Don't look UP"
]
w .show-and-run
What’s new: widget/button .set-text layout/spacer fyne/size
A button comes with a callback function that gets called when the button is clicked. does is a Rye function that creates a function with no arguments.

fyne: import\go "fyne"
app: import\go "fyne/app"
widget: import\go "fyne/widget"
container: import\go "fyne/container"
layout: import\go "fyne/layout"
lab: widget/label "I'm Waiting ..."
btn: widget/button "Click here" does { lab .set-text "Finally ..." }
box: container/vbox [
lab
layout/spacer
btn
]
a: app/new
w: a .window "Button"
w .resize fyne/size 200.0 100.0
w .set-content box
w .show-and-run
What’s new: widget/entry widget/select .text? .multi-line! .place-holder! dialog/show-information
Let’s now combine an entry widget, make it multi-line, a select field and a button to make our first potentially practical applet.

fyne: import\go "fyne"
app: import\go "fyne/app"
widget: import\go "fyne/widget"
container: import\go "fyne/container"
dialog: import\go "fyne/dialog"
w: app/new .window "Feedback"
ent: widget/entry
ent .multi-line! true
sel: widget/select [ "Happy" "Normal" "Confused" ] fn { x } { }
sel .place-holder! "How do you feel ..."
cont: container/vbox [
widget/label "Send us feedback:"
ent
sel
widget/button "Send" does {
msg: ent .text?
dialog/show-information "Sending" "Sending: " ++ msg w
}
]
w .set-content cont
w .show-and-run
What’s new: go (goroutines) with fyne/do
Rye inherits awesome goroutines from Go and Fyne works very nicely with them. For GUI updates from goroutines, we use fyne/do to ensure thread safety.

fyne: import\go "fyne"
app: import\go "fyne/app"
widget: import\go "fyne/widget"
lab: widget/label "<date & time>"
go does {
forever {
fyne/do does {
lab .set-text now .to-string
}
sleep 500
}
}
w: app/new .window "Date & Time"
w .set-content lab
w .show-and-run
What’s new: canvas/image-from-file .fill-mode!
In fact, goroutines get handy in a lot of cases. Imagine we are making an app that displays basic live information about our home. Let’s throw in an example of using an image also.
This app simulates reading outside temperature from a sensor that updates every 300 milliseconds.

fyne: import\go "fyne"
app: import\go "fyne/app"
widget: import\go "fyne/widget"
container: import\go "fyne/container"
canvas: import\go "fyne/canvas"
Sensors: context {
; dummy sensor
get-temperature: does { sleep 300 , 20 + random\integer 12 }
}
temp: widget/label "Reading ..."
img: canvas/image-from-file "home.png"
img .fill-mode! 2 ; original size
go does {
sleep 3000 ; waiting for sensors to wake up
forever {
temp-val:: Sensors/get-temperature .to-string ++ " °C"
fyne/do does {
temp .set-text "Outside temp: " ++ temp-val
}
sleep 1000
}
}
w: app/new .window "My Home"
w .set-content container/vbox [ img temp ]
w .show-and-run
What’s new: container/form widget/password-entry widget/check .disable .enable
Back to more static example. Fyne has this nice concept of forms, which is very handy. You can fill the right side of the form item with any widget.

fyne: import\go "fyne"
app: import\go "fyne/app"
widget: import\go "fyne/widget"
container: import\go "fyne/container"
dialog: import\go "fyne/dialog"
a: app/new
w: a .window "Form"
btn: widget/button "Sign up" does {
dialog/show-information "Success" "You rock!" w
}
btn .disable
chk: widget/check "I fully agree" fn { v } {
either v {
btn .enable
} {
btn .disable
}
}
cont: container/form [
"Username" widget/entry
"Password" widget/password-entry
"Terms" chk
"" btn
]
w .set-content cont
w .show-and-run
What’s new: widget/list fyne/size
Fyne handles lists in a quite simple and effective way. To construct a list you give constructor function widget/list 3 functions. First returns the number of items, second constructs the widget(s) for an item and in the third you update the given widgets for a given row index.
Below we create a list with 1 million line items and it works with no performance problems.

fyne: import\go "fyne"
app: import\go "fyne/app"
widget: import\go "fyne/widget"
lst: widget/list
does { 1000000 }
does { widget/label "num" }
fn { i item } { item .set-text to-string i + 1 }
w: app/new .window "1 million list"
w .resize fyne/size 220.0 200.0
w .set-content lst
w .show-and-run
What’s new: widget/list container/hbox .length? .objects? fyne/size
To me at least the main indicator of GUI framework’s elegance is to see how it handles lists with composed list items. Fyne’s approach does very good in this regard.

fyne: import\go "fyne"
app: import\go "fyne/app"
widget: import\go "fyne/widget"
container: import\go "fyne/container"
players: [ [ "WildJane" 5210 ] [ "MadBob" 4991 ] [ "GreeNoob" 12 ] ]
lst: widget/list
does { players .length? }
does { container/hbox [ widget/label "name" widget/label "score" ] }
fn { i box } {
player: players -> i
name-label: 0 <- box .objects?
score-label: 1 <- box .objects?
name-label .set-text 0 <- player
score-label .set-text to-string 1 <- player
}
a: app/new
w: a .window "Gastown bingo players"
w .resize fyne/size 220.0 200.0
w .set-content lst
w .show-and-run
What’s new: widget/table .row .col
Fyne also has tables that are conceptually very similar to Lists.

fyne: import\go "fyne"
app: import\go "fyne/app"
widget: import\go "fyne/widget"
tab: widget/table
does { [ 10 10 ] }
does { widget/label "..." }
fn { i o } {
r: 1 + i .row
c: 1 + i .col
o .set-text r * c .to-string
}
a: app/new
w: a .window "Multiplication table"
w .resize 330.0 400.0
w .set-content tab
w .show-and-run
What’s new: widget/progress-bar
Progress bars often have to be updated from another “thread”. With Fyne / Goroutines / Rye this is again no problem at all, using fyne/do for thread-safe updates.

fyne: import\go "fyne"
app: import\go "fyne/app"
widget: import\go "fyne/widget"
container: import\go "fyne/container"
ly: widget/label "Year:"
py: widget/progress-bar
ph: widget/progress-bar
pm: widget/progress-bar
ps: widget/progress-bar
cont: container/vbox [
ly
py
widget/label "Today's hours:"
ph
widget/label "Minutes:"
pm
widget/label "Seconds:"
ps
]
leap-year: fn { y } {
all [ y .mod 4 .equals 0 not y .mod 100 .equals 0 y .mod 400 .equals 0 ]
}
days-in: fn { y } {
either leap-year y { 366 } { 365 }
}
go does {
forever {
n: now
year: n .year
fyne/do does {
ly .set-text "Year " ++ year .to-string
py .set-value n .year-day / days-in year
ph .set-value n .hour / 24.0
pm .set-value n .minute / 60.0
ps .set-value n .second / 60.0
}
sleep 500
}
}
a: app/new
w: a .window "Percentage Clock"
w .resize 300.0 200.0
w .set-content cont
w .show-and-run
What’s new: widget/button-with-icon .objects? .deref .on-changed! .on-tapped! .on-submitted!
This is a very simple work in progress app with simple state management. This example demonstrates more complex state management and list interactions.

fyne: import\go "fyne"
app: import\go "fyne/app"
widget: import\go "fyne/widget"
container: import\go "fyne/container"
theme: import\go "fyne/theme"
layout: import\go "fyne/layout"
; Simple data management
tasks: ref [
[ false "Goat cheese" ]
[ false "Eggs" ]
[ false "Oats" ]
[ false "Anchovies" ]
[ false "Bread" ]
[ false "A4 paper" ]
]
; Rye is mostly immutable, we are working
; on state management solution
Data: context {
add!: fn { task } {
print "* Adding" probe task
}
remove!: fn { idx } {
print "* Removing" probe idx
}
check!: fn { idx val } {
print "* Checking" probe [ idx val ]
}
}
lst: widget/list
does { tasks .deref .length? }
does {
container/hbox [
widget/check "" fn { v } { } ; will be updated with proper callback
widget/label ""
layout/spacer
widget/button-with-icon "" theme/delete-icon fn { } { } ; will be updated
]
}
fn { i box } {
tasks .deref -> i :task
box .objects? -> 0 :chk
box .objects? -> 1 :lbl
box .objects? -> 3 :btn
chk .set-checked 0 <- task
lbl .set-text 1 <- task
; Update callbacks with current index
chk .on-changed! fn { v } { Data/check! i v }
btn .on-tapped! fn { } { Data/remove! i , lst .refresh }
}
input: widget/entry
input .set-place-holder "Add to list here ..."
input .on-submitted! fn { x } {
Data/add! x
input .set-text ""
lst .refresh
}
cont: container/border nil input nil nil [ lst ]
a: app/new
w: a .window "Shopping List"
w .resize fyne/size 300.0 300.0
w .set-content cont
w .show-and-run
What’s new: container/app-tabs container/tab-item widget/slider widget/separator
This example demonstrates a modern tabbed interface with multiple settings panels. It showcases how to organize complex interfaces using tabs, sliders for continuous values, and various interactive controls.

fyne: import\go "fyne"
app: import\go "fyne/app"
widget: import\go "fyne/widget"
container: import\go "fyne/container"
theme: import\go "fyne/theme"
a: app/new
w: a .window "Settings Panel"
; General settings tab content
private {
volume-slider: widget/slider 0.0 100.0
volume-slider .set-value 75.0
volume-label: widget/label "Volume: 75%"
volume-slider .on-changed! closure { v } {
volume-label .set-text "Volume: " ++ v ++ "%"
}
notifications-check: widget/check "Enable notifications" fn { v } {
print "Notifications:" v
}
notifications-check .set-checked true
auto-save-check: widget/check "Auto-save documents" fn { v } {
print "Auto-save:" v
}
container/vbox [
widget/label "Audio Settings"
volume-slider
volume-label
widget/separator
widget/label "Application Settings"
notifications-check
auto-save-check
]
} :general-content
; About tab content
about-content: container/vbox [
widget/icon theme/info-icon
widget/label "Tabbed Settings Demo"
widget/separator
widget/label "Version 1.0.0"
]
; Create tab container
tabs: container/app-tabs [
container/tab-item "General" general-content
container/tab-item "About" about-content
]
w .set-content tabs
w .resize fyne/size 400.0 350.0
w .show-and-run
What’s new: widget/rich-text-from-markdown .set-min-size .parse-markdown
This example shows how to create a rich text viewer with markdown support, and interactive controls. It demonstrates document-style applications.

fyne: import\go "fyne"
app: import\go "fyne/app"
widget: import\go "fyne/widget"
container: import\go "fyne/container"
theme: import\go "fyne/theme"
a: app/new
w: a .window "Micro book reader"
; Create rich text widget
rich-text: widget/rich-text-from-markdown "loading ..."
rich-text .wrapping! 3
page-label: widget/label "page unknown ..."
var 'page 1
next-page: closure { } { if page < 3 { change! inc page 'page } }
back-page: closure { } { if page > 1 { change! decr page 'page } }
load-page: closure { } {
rich-text .parse-markdown Read to-file join [ "page" page ".md" ]
page-label .set-text join [ "Page: " page ]
}
load-page
; Create toolbar
toolbar: container/hbox [
widget/button-with-icon "Previous" theme/navigate-back-icon does {
back-page
load-page
}
widget/button-with-icon "Next" theme/navigate-next-icon does {
next-page
load-page
}
]
; Main layout using border container
main-content: container/border
toolbar ; top
container/hbox [ ; bottom
page-label
]
nil ; left
nil ; right
[ rich-text ] ; center (as list for multiple items)
w .set-content main-content
w .resize fyne/size 500.0 500.0
w .show-and-run
What’s new: https // Get .clipboard .set-content fix
This one, I actually use daily. HTTP call happens in a goroutine so it doesn’t block UI rendering and it refreshes the UI using fyne/do to prevent race conditions.
Html parsing is somewhat patched together, but “it works”, and it a simple demo of failure handling.

fyne: import\go "fyne"
app: import\go "fyne/app"
widget: import\go "fyne/widget"
container: import\go "fyne/container"
w: app/new .window "Get my IP"
lab: widget/label "<retrieving ...>"
btn: widget/button "Copy" closure { } {
w .clipboard .set-content lab .text?
}
get-ip: does {
Get https://ifconfig.me
|^fix { "couldn't load ifconfig.me" }
|html->markdown
|^fix { "couldn't parse html" }
|Submatch?* regexp "IP Address \*\*([0-9.]+)\*\*"
|^fix { "couldn't find the IP pattern" }
}
go does {
forever {
fyne/do does {
lab .set-text get-ip
}
sleep 1 .minutes
}
}
w .set-content container/hbox [ lab btn ]
w .show-and-run
These examples demonstrate the new Rye-fyne syntax with explicit imports and namespaced function calls.
The new system is somewhat more verbose, but provides better clarity about which Go modules are being used and makes the code more maintainable and contexts explorable in Rye console.
You can still find the old examples for comparison.
Key changes in the new system:
import\go statements for each Fyne modulewidget/label instead of label, container/vbox instead of v-boxfyne/do for GUI updates from goroutinesCreated: 10/26/2025