Migrating from State Pages

Move one page at a time: build the web page at a new URL next to the old one, compare the two, then move the URL over. State pages and UI components keep running, so nothing has to happen at once. The tables below show which web block replaces which, and what has no direct replacement.

What actually changes

State pageWeb page
What the browser getsA React application that builds the pageFinished HTML and a script under 4 KB compressed
The markup comes fromA tree of blocks, one per elementA template, usually one block
Interaction runs overA WebSocket, with state held between requestsA plain POST. The server holds nothing between requests
Typing into a formGoes through stateStays in the browser
An answer holdsState updates the page reacts toPieces of HTML to put in place

Which web block replaces which

Pages and rendering

State or UI component blockBecomes
state_page, uicomponent_pageweb_page. The same event, scopes and l10n, and the same connection, params and query pins.
state_render_page, uicomponent_page_renderweb_render, with a template where the content pin was.
state_div, state_h1, state_button and every other element blockThe plain tag in the template: <div>, <h1>, <button>.
state_htmlPlain markup in the template, or {{{value}}} for HTML a flow built.
State templates in Manage TemplatesA stored web template used as a layout, with {{{body}}} and {{{head}}}.
uicomponent_update_meta_tagsThe title, description, image and meta pins. See SEO and Sharing.

Lists and tables

State or UI component blockBecomes
state_foreach_div, state_foreach_ul, state_foreach_ol, state_foreach_tr, state_foreach_tbody, state_foreach_thead, uicomponent_foreachweb_foreach when an action changes the rows later, or {{for item in items}} in the template when they only show. See Lists and Tables.
state_datatable, state_advanced_datatableweb_foreach into a <table>, plus an action for each thing the table does. See below.
state_set_datatable_refresh, state_datatable_pushweb_replace and web_insert of just that row.

Events and state

State or UI component blockBecomes
stateevents_onclick, stateevents_onchange, stateevents_onenterdata-action="name" on the element, and a web_action with that name. A button or link fires on click, a field on change, and a form on submit, Enter included.
state_on_state_changeA field with data-action, adding data-trigger="input" to run while typing.
state_trigger_custom_function and its state_inline_function_executeA web_action. A shared piece of logic becomes an ordinary function that the action calls.
state_backend_requestNothing: the action is already on the server. To fill a part of the page after it loads, use data-trigger="load".
state_update_state, state_update_state_fields, state_return_state, state_increment_state, global stateA value on the server, usually in a storage, and a web_update or web_replace of the place that shows it. Read the section below.
state_is_true, state_is_empty, state_branch{{if}} in the template, or a branch in the flow when the server decides.

Forms, navigation and the rest

State or UI component blockBecomes
state_form, state_input, state_submit_buttonA plain <form data-action="name"> with plain inputs. See Forms and Validation.
state_form_getThe form out-pin of web_action.
Field validation shown in the formrequired, pattern and the like in the markup, and web_errors for what only the server knows.
state_flow_modal, state_return_modal, state_close_flow_modal, uicomponent_modalMarkup that is on the page already, opened with data-toggle="#dialog" with no request. For content the server must build, either a page of its own, or an action that answers web_replace of a hidden placeholder with the visible dialog.
state_translate_key, state_translate_dynamic{{t "key"}} and {{t key}}. Your translation keys stay as they are. See Languages.
state_redirect, state_redirect_to_page, uicomponent_go_to_pageweb_redirect, with a path you write out.
state_refresh_pageA web_redirect to the page's own path, or a web_render in the action.
state_script, state_custom_function_executionYour own <script nonce="{{nonce}}">, or a file the layout links.
state_set_cookieA Set-Cookie in the headers pin of web_render, or a line in your script. Reading stays httpconnection_get_cookie, which reads the ui- prefix for you.
state_input_file, state_get_uploaded_fileThe upload endpoints, with the result passed to the action. A web action refuses files.

State becomes a value on the server and a piece of HTML

This is the one change in thinking. On a state page the browser holds state and the page reacts to it. On a web page the page shows what the server rendered, and an action changes something on the server and sends back what now looks different.

State page

A click updates cart_count in global state, and every component that reads it renders again.

Web page

A click runs an action that writes the cart to a storage and answers web_update(target "#cart-count", html: count). If two places show the count, the action answers two updates, or the places share one element.

Purely visual state needs no server at all: data-toggle for show and hide, and a few lines of your own script for the rest.

What has no one-to-one replacement

On a state pageOn a web page
Live updates pushed over a WebSocket, such as state_broadcast_subscribeA web page has no WebSocket of its own. Poll with data-trigger="every 10s", which pauses while the page is not visible. For true push, open a socket in your own script and call RUALWeb.apply(ops). A chat or a live dashboard may be better off staying a state page for now.
The built-in datatable: sorting, filtering, paging and selection out of the boxYou write what you need: a search with a sort from query or a data-param-*, links or an action for paging, and checkboxes in a form for selection. It is more work up front, and every part is plain HTML you control. A heavy admin table is a fair thing to migrate last.
Client state shared between componentsRendered by the server, or held in your own script. A page that behaves like a spreadsheet or an editor is mostly client state, and suits this model least.
Custom React componentsYour own script with a nonce, mounted on an element in the template. There is no hydration step. You bring and load the library yourself, from your own files or over https:.
Modals as flows of their own, opened by name from anywhereNo registry of modals. A dialog is markup on the page, a page, or the answer of an action in this blueprint.
Actions across blueprintsAn action belongs to its page's blueprint. Put shared logic in a function and call it from an action in each.

A safe order to move one page in

  1. Write down what the page does

    List what it reads, what each click and form does, who may open it, and what it shows search engines. This list is your test later. Start with a page that mostly shows things, and leave the most interactive ones for last.

  2. Build the web page at a new URL

    Make a new blueprint with a web_page at a URL nobody uses yet, such as new/pricing, with the same scopes as the old page. Set both in the code view, as in Your First Page, since the canvas has no URL button on this block yet. Keep this URL out of search with the meta pin: {"robots": "noindex"}. The old page is untouched, so nothing can break yet.

  3. Move what it shows first

    Turn the shell into a stored layout and the elements into a template, and call the same functions and storages. Compare the rendered result with the old page, in each language and both signed in and signed out.

  4. Then its actions, one at a time

    One web_action per intent. Test each with real data in a test document. If the new blueprint has pages with different scopes, give each action its own.

  5. Compare, side by side

    Go through your list on both URLs. Check view source for the title, description and canonical, and test a form with bad input and with JavaScript off if that matters for this page.

  6. Move the URL over

    In one sitting: give the old page another URL or deactivate its blueprint, set the web page's event and l10n to the old URLs, remove the noindex, save and activate. Do not leave two active pages on one URL, because which one answers is not something to rely on. The address stays the same, so links and search results keep working.

  7. Watch, and keep the way back open

    Keep the old blueprint for a while. To go back, swap the URLs again and save: saving is what publishes. Remove the old blueprint only when you are sure.

State pages keep running Nothing here forces a move. Existing state pages and UI components keep working next to web pages on the same cluster, so you can move pages when it suits you, and in any order.

Next steps

Your First Page Get a feel for the blocks on a small page first. Tutorial: A Small Webshop Lists, actions and forms working together. Actions Where your state handlers end up.

Frequently asked

Are RUAL state pages being removed?

They are deprecated, not removed: existing state pages and UI components keep running. New work should use the web blocks, and the migration guide gives a safe order to move a page across.

What replaces state_page and state_render_page?

web_page and web_render. Where a state page keeps state in the browser and re-renders it, a web page renders on the server and answers an action with only the parts that changed: web_foreach for lists, web_action for buttons and forms, and web_replace, web_remove, web_insert and web_update for the updates.