diff --git a/basis/furnace/chloe-tags/chloe-tags.factor b/basis/furnace/chloe-tags/chloe-tags.factor
index d7d9ae9ebb..8003ab208b 100644
--- a/basis/furnace/chloe-tags/chloe-tags.factor
+++ b/basis/furnace/chloe-tags/chloe-tags.factor
@@ -81,11 +81,18 @@ CHLOE: a
CHLOE: base
compile-a-url [ [XML /> XML] ] [xml-code] ;
+: hidden-nested-fields ( -- xml )
+ nested-forms get " " join f like nested-forms-key
+ hidden-form-field ;
+
+: render-hidden ( for -- xml )
+ "," split [ hidden render>xml ] map ;
+
: compile-hidden-form-fields ( for -- )
'[
- _ [ "," split [ hidden render>xml ] map ] [ f ] if*
- nested-forms get " " join f like nested-forms-key hidden-form-field>xml
- [ [ modify-form ] each-responder ] with-string-writer
+ _ render-hidden
+ hidden-nested-fields
+ form-modifications
[XML <-><-><->
XML]
] [code] ;
diff --git a/basis/furnace/utilities/utilities-docs.factor b/basis/furnace/utilities/utilities-docs.factor
index d2291786df..62f73d4f09 100644
--- a/basis/furnace/utilities/utilities-docs.factor
+++ b/basis/furnace/utilities/utilities-docs.factor
@@ -20,8 +20,8 @@ HELP: each-responder
{ $description "Applies the quotation to each responder involved in processing the current request." } ;
HELP: hidden-form-field
-{ $values { "value" string } { "name" string } }
-{ $description "Renders an HTML hidden form field tag." }
+{ $values { "value" string } { "name" string } { "xml" "an XML chunk" } }
+{ $description "Renders an HTML hidden form field tag as XML." }
{ $notes "This word is used by session management, conversation scope and asides." }
{ $examples
{ $example
@@ -38,7 +38,7 @@ HELP: link-attr
{ $examples "Conversation scope adds attributes to link tags." } ;
HELP: modify-form
-{ $values { "responder" "a responder" } }
+{ $values { "responder" "a responder" } { "xml/f" "an XML chunk or f" } }
{ $contract "Emits hidden form fields using " { $link hidden-form-field } "." }
{ $notes "This word is called by " { $link "html.templates.chloe.tags.form" } "." }
{ $examples "Session management, conversation scope and asides use hidden form fields to pass state." } ;
diff --git a/basis/furnace/utilities/utilities.factor b/basis/furnace/utilities/utilities.factor
index a2d4c4d996..2f998e039a 100755
--- a/basis/furnace/utilities/utilities.factor
+++ b/basis/furnace/utilities/utilities.factor
@@ -77,18 +77,18 @@ GENERIC: link-attr ( tag responder -- )
M: object link-attr 2drop ;
-GENERIC: modify-form ( responder -- )
+GENERIC: modify-form ( responder -- xml/f )
-M: object modify-form drop ;
+M: object modify-form f ;
-: hidden-form-field>xml ( value name -- xml )
+: form-modifications ( -- xml )
+ [ [ modify-form [ , ] when ] each-responder ] { } make ;
+
+: hidden-form-field ( value name -- xml )
over [
[XML name=<->/> XML]
] [ drop ] if ;
-: hidden-form-field ( value name -- )
- hidden-form-field>xml write-xml ;
-
: nested-forms-key "__n" ;
: request-params ( request -- assoc )