Migration Guides
Migration guides for Kotlin SDKs.
Migration guide for v1.x.x to v2.x.x
This guide explains how to migrate your apps after the v2 release’s package and artifact restructure. It covers three paths: staying on v1 via legacy modules, migrating from v1 to v2, and moving from v2-beta to the final v2.
For feature-level differences and what’s new, see: What’s new in 2.0.0
What changed at a glance
- Dependencies: use standard
io.joyfill:*
artifacts for v2, orio.joyfill:legacy-*
to remain on v1 (details, stay on v1) - Package names: v2-beta code under
joyfill2
moved tojoyfill
in v2 — rename imports fromjoyfill2
→joyfill
(details) - Event API: legacy
FieldEvent
has been replaced byComponentEvent.*
— useComponentEvent.FieldEvent
(standalone fields) andComponentEvent.CellEvent
(table cells) (details) - Behavior notes: most imports remain identical for v1→v2 because the package is still
joyfill
; some v1 UI components may not exist in v2 — check v2 docs for alternatives (details)
Dependencies changes
- Replace legacy artifacts with the standard ones:
- io.joyfill:legacy-compose → io.joyfill:compose
- io.joyfill:legacy-models → io.joyfill:models
- io.joyfill:legacy-builder → io.joyfill:builder
- io.joyfill:legacy-api → io.joyfill:api
Gradle example (Kotlin DSL)
// BEFORE (v1)
dependencies {
implementation("io.joyfill:legacy-compose:<version>")
implementation("io.joyfill:legacy-models:<version>")
implementation("io.joyfill:legacy-builder:<version>")
implementation("io.joyfill:legacy-api:<version>")
}
// AFTER (v2)
dependencies {
implementation("io.joyfill:compose:<version>")
implementation("io.joyfill:models:<version>")
implementation("io.joyfill:builder:<version>")
implementation("io.joyfill:api:<version>")
}
Imports and usage
- Keep using
joyfill.*
imports. - Example usage:
Form(
editor = rememberEditor(document = doc),
mode = Mode.fill,
)
FieldEvent → ComponentEvent.* (events)
Summary
- v1 used a single
FieldEvent
for all field callbacks. - v2 unifies events under
ComponentEvent.*
with two concrete types:ComponentEvent.FieldEvent<E>
for standalone fields (text, number, image, signature, etc.)ComponentEvent.CellEvent<E>
for table cells (row-aware events)
- Handler parameter types changed accordingly in
Form
/components.
Why this changed
- The v2 architecture standardizes how components emit and consume events and makes table events first-class by including row/column context.
API mapping
- v1 (legacy):
joyfill.FieldEvent
- v2:
joyfill.ComponentEvent.FieldEvent
andjoyfill.ComponentEvent.CellEvent
Key properties mapping
- Common in both versions:
fieldId
,fieldIdentifier
,pageId
,id
(document id),identifier
(document identifier),fileId
,fieldPositionId
- v2 additions/notes:
source
: the typed editor instance (e.g.,ImageEditor
,TextEditor
)multi
: available when the underlying component is a file component (e.g., image with multi-upload)- Cell-only:
rowIds
,columnId
,schemaId
,parentPath
Before → After examples
- General field handlers (change/focus/blur)
- v1 (legacy Form):
Form(
// ...
onFieldChange = { e: FieldEvent -> /* use e.fieldId, e.pageId, ... */ },
onFocus = { e: FieldEvent -> /* ... */ },
onBlur = { e: FieldEvent -> /* ... */ },
)
- v2 (Form/components):
Form(
// ...
onFieldChange = { e: ComponentEvent<*> ->
when (e) {
is ComponentEvent.FieldEvent<*> -> { /* standalone field */ }
is ComponentEvent.CellEvent<*> -> { /* table cell */ }
}
},
onFocus = { e: ComponentEvent<*> -> /* same pattern as above */ },
onBlur = { e: ComponentEvent<*> -> /* same pattern as above */ },
)
- File upload/capture handlers (image/signature/barcode)
- v1:
Form(
onUpload = { e: FieldEvent -> listOf("https://.../file1") },
onCapture = { e: FieldEvent -> "barcode-or-text" },
)
- v2:
Form(
onUpload = { e: ComponentEvent<joyfill.editors.file.AbstractFileEditor> ->
// Optional: narrow by editor type
when (val src = e.source) {
is joyfill.editors.image.ImageEditor -> listOf("https://.../img1")
is joyfill.editors.signature.SignatureEditor -> listOf("https://.../sig1")
else -> emptyList()
}
},
onCapture = { e: ComponentEvent<joyfill.editors.components.AbstractCompStringEditor> ->
// Example for barcode/text-capable components
when (e.source) {
is joyfill.editors.barcode.BarcodeEditor -> "scanned-barcode"
else -> null
}
},
)
- Table cell events
- v2 introduces
CellEvent
for row-aware callbacks used by table components and their editors:
val handler: (ComponentEvent.CellEvent<*>) -> Unit = { e ->
val rowIds = e.rowIds // one or more row ids affected
val columnId = e.columnId
val fieldId = e.fieldId
// handle per-row updates, analytics, etc.
}
Minimal refactor recipe
- Replace callback parameter types:
FieldEvent
→ComponentEvent<*>
when used at the Form level- For table-specific handlers: prefer
ComponentEvent.CellEvent<*>
- For single-field components: prefer
ComponentEvent.FieldEvent<*>
- Update
when
branches to handle bothFieldEvent
andCellEvent
where applicable. - If you previously relied on
attachments
in events: v2 surfaces file uploads through file editors and returns should come from your handler;attachments
on events is not required in typical flows.
Runtime checks and generics
ComponentEvent
is generic on the editor type (E : ComponentEditor
). When you need editor-specific behavior, checke.source
withis ImageEditor
etc.
References
- v2 event system overview: architecture/v2/event-system.md
FAQ
Q: Why do both v1 and v2 have joyfill.Form
?
A: v1 code was moved into legacy-*
modules but retained the exact same package names for easier migration. Which Form
you get depends on whether your dependency is from legacy-*
or from the standard modules.
Q: How do I verify I’m on v2?
- Ensure you’re using the standard artifacts (no
legacy-
prefix). - Confirm your imports are
joyfill.*
and that you do NOT havejoyfill2.*
anywhere.
Q: Any behavioral differences?
- Some components may be missing in v2 compared to v1, but v2 offers many new features and improved architecture. See the v2 docs for details.
Related Documentation
- Architecture Overview: architecture/README.md
- V2 Overview: architecture/v2/README.md
- V2 UI Components: architecture/v2/ui-components.md
- V2 Editor Pattern: architecture/v2/editor-pattern.md
- V2 API Integration: architecture/v2/api-integration.md
Important context
- Prior to v2 release, we had:
- v1 under the
joyfill
package - v2-beta under the
joyfill2
package
- v1 under the
- With the v2 release:
- v2 now lives under the
joyfill
package - Legacy v1 classes were moved into separate legacy modules, but kept the same
joyfill
package names for source compatibility - Practically, both legacy and v2 expose symbols like
joyfill.Form
. Which one you use depends on the dependency you add.
- v2 now lives under the
What this means for you
- V1 → V2: swap your dependencies from legacy modules to v2 modules. Most imports remain identical because the package is still
joyfill
. - V2-beta → V2: rename your imports from
joyfill2
tojoyfill
. No other code changes should be required.
Stay on v1 (Legacy Modules)
[!CAUTION] This is not RECOMMENDED. This is a temporary path for those who want to continue using v1 for a short period of time.
Summary
- If you want to continue using the legacy v1 implementation for a period, switch your dependencies to the
legacy-*
artifacts. - Imports remain
joyfill.*
because legacy modules preserve the same package names.
Dependencies
- Replace standard artifacts with legacy ones if you were previously on the standard artifacts but wish to remain on v1:
io.joyfill:compose
→io.joyfill:legacy-compose
io.joyfill:models
→io.joyfill:legacy-models
io.joyfill:builder
→io.joyfill:legacy-builder
io.joyfill:api
→io.joyfill:legacy-api
Gradle example (Kotlin DSL)
// BEFORE (standard artifacts)
dependencies {
implementation("io.joyfill:compose:<version>")
implementation("io.joyfill:models:<version>")
implementation("io.joyfill:builder:<version>")
implementation("io.joyfill:api:<version>")
}
// AFTER (stay on v1 legacy)
dependencies {
implementation("io.joyfill:legacy-compose:<version>")
implementation("io.joyfill:legacy-models:<version>")
implementation("io.joyfill:legacy-builder:<version>")
implementation("io.joyfill:legacy-api:<version>")
}
Verification
- Ensure your imports remain
joyfill.*
and notjoyfill2.*
. - Confirm you’re pulling
legacy-*
artifacts in your dependency tree.
Notes
- This path is intended for temporary continuity. Plan to migrate to v2 soon.
Updated 12 days ago