diff --git a/collects/web-server/default-web-root/htdocs/servlets/examples/static.html b/collects/web-server/default-web-root/htdocs/servlets/examples/static.html
new file mode 100644
index 0000000000..b211384e7a
--- /dev/null
+++ b/collects/web-server/default-web-root/htdocs/servlets/examples/static.html
@@ -0,0 +1,11 @@
+
+
Title
+
+
+
+
diff --git a/collects/web-server/default-web-root/htdocs/servlets/examples/template-full.ss b/collects/web-server/default-web-root/htdocs/servlets/examples/template-full.ss
new file mode 100644
index 0000000000..5643aab6ad
--- /dev/null
+++ b/collects/web-server/default-web-root/htdocs/servlets/examples/template-full.ss
@@ -0,0 +1,13 @@
+#lang scheme
+(require web-server/templates
+ web-server/http)
+(provide (all-defined-out))
+(define interface-version 'v1)
+(define timeout +inf.0)
+
+(define (start initial-request)
+ (make-response/full
+ 200 "Okay"
+ (current-seconds) TEXT/HTML-MIME-TYPE
+ empty
+ (list (include-template "static.html"))))
\ No newline at end of file
diff --git a/collects/web-server/default-web-root/htdocs/servlets/examples/template-simple.ss b/collects/web-server/default-web-root/htdocs/servlets/examples/template-simple.ss
new file mode 100644
index 0000000000..61ef233ab8
--- /dev/null
+++ b/collects/web-server/default-web-root/htdocs/servlets/examples/template-simple.ss
@@ -0,0 +1,8 @@
+#lang scheme
+(require web-server/templates)
+(provide (all-defined-out))
+(define interface-version 'v1)
+(define timeout +inf.0)
+
+(define (start initial-request)
+ (list #"text/html" (include-template "static.html")))
\ No newline at end of file
diff --git a/collects/web-server/default-web-root/htdocs/servlets/examples/template-xexpr.ss b/collects/web-server/default-web-root/htdocs/servlets/examples/template-xexpr.ss
new file mode 100644
index 0000000000..28407c62ca
--- /dev/null
+++ b/collects/web-server/default-web-root/htdocs/servlets/examples/template-xexpr.ss
@@ -0,0 +1,11 @@
+#lang scheme
+(require web-server/templates
+ xml)
+(provide (all-defined-out))
+(define interface-version 'v1)
+(define timeout +inf.0)
+
+(define (start initial-request)
+ `(html (pre ,(include-template "static.html"))
+ "versus"
+ ,(make-cdata #f #f (include-template "static.html"))))
\ No newline at end of file
diff --git a/collects/web-server/scribblings/templates.scrbl b/collects/web-server/scribblings/templates.scrbl
new file mode 100644
index 0000000000..a42b233b04
--- /dev/null
+++ b/collects/web-server/scribblings/templates.scrbl
@@ -0,0 +1,228 @@
+#lang scribble/doc
+@(require "web-server.ss")
+@(require (for-label web-server/servlet
+ web-server/templates
+ scheme/list
+ xml))
+
+@(define xexpr @tech[#:doc '(lib "xml/xml.scrbl")]{X-expression})
+@(define at-reader-ref @secref[#:doc '(lib "scribblings/scribble/scribble.scrbl")]{reader})
+
+@title[#:tag "templates"]{Templates}
+
+@defmodule[web-server/templates]
+
+The @web-server provides a powerful Web template system for separating the presentation logic of a Web application
+and enabling non-programmers to contribute to PLT-based Web applications.
+
+@local-table-of-contents[]
+
+@section{Static}
+
+Suppose we have a file @filepath{static.html} with the contents:
+@verbatim[#:indent 2]|{
+
+ Fastest Templates in the West!
+
+ Bang!
+ Bang!
+
+
+}|
+
+If we write the following in our code:
+@schemeblock[
+ (include-template "static.html")
+]
+
+Then the contents of @filepath{static.html} will be read @emph{at compile time} and compiled into a
+Scheme program that returns the contents of @filepath{static.html} as a string:
+@schemeblock[
+ "\n Fastest Templates in the West!\n \n Bang!
\n Bang!
\n \n"
+]
+
+@section{Dynamic}
+
+@scheme[include-template] gives the template access to the @emph{complete lexical context} of the including program. This context can be
+accessed via the @at-reader-ref syntax. For example, if @filepath{simple.html} contains:
+@verbatim[#:indent 2]|{
+
+ Fastest @thing in the West!
+
+ Bang!
+ Bang!
+
+
+}|
+
+Then
+@schemeblock[
+ (let ([thing "Templates"])
+ (include-template "simple.html"))
+]
+evaluates to the same content as the static example.
+
+There is no constraints on the values, the way they are used, or the way they are defined, that are made accessible to the template.
+For example,
+@schemeblock[
+ (define (fast-template thing)
+ (include-template "simple.html"))
+
+ (fast-template "Templates")
+ (fast-template "Noodles")
+]
+evalutes to two strings with the predictable contents:
+@verbatim[#:indent 2]|{
+
+ Fastest Templates in the West!
+
+ Bang!
+ Bang!
+
+
+}|
+
+and
+
+@verbatim[#:indent 2]|{
+
+ Fastest Noodles in the West!
+
+ Bang!
+ Bang!
+
+
+}|
+
+@section{Gotchas}
+
+One of the most important things to remember about the @at-reader-ref syntax is that the @"@" symbol must be escaped in content:
+@verbatim[#:indent 2]|{
+
+ Fastest @"@"s in the West!
+
+ Bang!
+ Bang!
+
+
+}|
+
+The other gotcha is that since the template is compiled into a Scheme program, only its results will be printed. For example, suppose
+we have the template:
+@verbatim[#:indent 2]|{
+
+ @for[([c clients])]{
+ @(car c), @(cdr c) |
+ }
+
+}|
+
+If this is included in a lexical context with @scheme[clients] bound to @scheme[(list (cons "Young" "Brigham") (cons "Smith" "Joseph"))],
+then the template will be printed as:
+@verbatim[#:indent 2]|{
+
+}|
+because @scheme[for] does not return the value of the body.
+Suppose that we change the template to use @scheme[for/list] (which combines them into a list):
+@verbatim[#:indent 2]|{
+
+ @for/list[([c clients])]{
+ @(car c), @(cdr c) |
+ }
+
+}|
+
+Now the result is:
+@verbatim[#:indent 2]|{
+
+}|
+because only the final expression of the body of the @scheme[for/list] is included in the result. We can capture all the sub-expressions
+by using @scheme[list] in the body:
+@verbatim[#:indent 2]|{
+
+ @for/list[([c clients])]{
+ @list{
+ @(car c), @(cdr c) |
+ }
+ }
+
+}|
+Now the result is:
+@verbatim[#:indent 2]|{
+
+ Young, Brigham |
+ Smith, Joseph |
+
+}|
+
+The templating library provides a syntactic form to deal with this issue for you called @scheme[in]:
+@verbatim[#:indent 2]|{
+
+ @in[c clients]{
+ @(car c), @(cdr c) |
+ }
+
+}|
+Notice how it also avoids the absurd amount of punctuation on line two.
+
+@section{HTTP Responses}
+
+The quickest way to generate an HTTP response from a template is using the @scheme[list] response type:
+@schemeblock[
+ (list #"text/html" (include-template "static.html"))
+]
+
+If you want more control then you can generate a @scheme[response/full] struct:
+@schemeblock[
+ (make-response/full
+ 200 "Okay"
+ (current-seconds) TEXT/HTML-MIME-TYPE
+ empty
+ (list (include-template "static.html")))
+]
+
+Finally, if you want to include the contents of a template inside a larger @xexpr :
+@schemeblock[
+ `(html ,(include-template "static.html"))
+]
+will result in the literal string being included (and entity-escaped). If you actually want
+the template to be unescaped, then create a @scheme[cdata] structure:
+@schemeblock[
+ `(html ,(make-cdata #f #f (include-template "static.html")))
+]
+
+@section{API Details}
+
+@defform[(include-template path)]{
+ Compiles the template at @scheme[path] using the @at-reader-ref syntax within the enclosing lexical context.
+
+ Example:
+ @schemeblock[
+ (include-template "static.html")
+ ]
+}
+
+@defform[(in x xs e ...)]{
+ Expands into
+ @schemeblock[
+ (for/list ([x xs])
+ (list e ...))
+ ]
+
+ Template Example:
+ @verbatim[#:indent 2]|{
+ @in[c clients]{
+ @(car c), @(cdr c) |
+ }
+ }|
+
+ Scheme Example:
+ @schemeblock[
+ (in c clients "" (car c) ", " (cdr c) " |
")
+ ]
+}
+
\ No newline at end of file
diff --git a/collects/web-server/scribblings/writing.scrbl b/collects/web-server/scribblings/writing.scrbl
index 56b08917a6..2f3de6e8ef 100644
--- a/collects/web-server/scribblings/writing.scrbl
+++ b/collects/web-server/scribblings/writing.scrbl
@@ -165,4 +165,5 @@ things in the Web Language, they are sensitive to source code modification.
@; ------------------------------------------------------------
@include-section["formlets.scrbl"]
+@include-section["templates.scrbl"]
@include-section["managers.scrbl"]
diff --git a/collects/web-server/template/examples/basic.html b/collects/web-server/template/examples/basic.html
index c8104be091..ed0197c19b 100644
--- a/collects/web-server/template/examples/basic.html
+++ b/collects/web-server/template/examples/basic.html
@@ -2,13 +2,11 @@
@title
- @in[c @clients]{
- @list{
+ @in[c clients]{
@(client-surname c), @(client-firstname c) |
@(client-email c) |
- }
}
diff --git a/collects/web-server/template/examples/run.ss b/collects/web-server/template/examples/run.ss
index 79ec4e4f5a..ec85009b99 100644
--- a/collects/web-server/template/examples/run.ss
+++ b/collects/web-server/template/examples/run.ss
@@ -1,33 +1,5 @@
#lang scheme
-(require xml
- scribble/text
- scheme/port)
-
-(define-syntax include-template
- (syntax-rules ()
- [(_ p)
- (with-output-to-string
- (lambda ()
- (output (include/text p))))]))
-
-(define-syntax include-template/xexpr
- (syntax-rules ()
- [(_ p)
- (string->xexpr (include-template p))]))
-
-(define (string->xexpr s)
- (with-input-from-string
- s
- (lambda ()
- (xml->xexpr (document-element (read-xml))))))
-
-(define-syntax in
- (syntax-rules ()
- [(_ x xs e ...)
- (for/list ([x xs])
- e ...)]))
-
-; Examples
+(require web-server/templates)
(include-template "static.html")
diff --git a/collects/web-server/templates.ss b/collects/web-server/templates.ss
new file mode 100644
index 0000000000..5ee1b8798f
--- /dev/null
+++ b/collects/web-server/templates.ss
@@ -0,0 +1,31 @@
+#lang scheme
+(require xml
+ scribble/text
+ scheme/port)
+
+(define-syntax include-template
+ (syntax-rules ()
+ [(_ p)
+ (with-output-to-string
+ (lambda ()
+ (output (include/text p))))]))
+
+(define-syntax include-template/xexpr
+ (syntax-rules ()
+ [(_ p)
+ (string->xexpr (include-template p))]))
+
+(define (string->xexpr s)
+ (with-input-from-string
+ s
+ (lambda ()
+ (xml->xexpr (document-element (read-xml))))))
+
+(define-syntax in
+ (syntax-rules ()
+ [(_ x xs e ...)
+ (for/list ([x xs])
+ (list e ...))]))
+
+(provide include-template
+ in)
\ No newline at end of file