diff --git a/template.go b/template.go
index 6df25fd9..0feffbe3 100644
--- a/template.go
+++ b/template.go
@@ -31,6 +31,7 @@ func init() {
 	beegoTplFuncMap["str2html"] = Str2html
 	beegoTplFuncMap["htmlquote"] = Htmlquote
 	beegoTplFuncMap["htmlunquote"] = Htmlunquote
+	beegoTplFuncMap["renderform"] = RenderForm
 }
 
 // AddFuncMap let user to register a func in the template
diff --git a/utils.go b/utils.go
index e0ead258..0afcb4ba 100644
--- a/utils.go
+++ b/utils.go
@@ -178,7 +178,7 @@ func inSlice(v string, sl []string) bool {
 func ParseForm(form url.Values, obj interface{}) error {
 	objT := reflect.TypeOf(obj)
 	objV := reflect.ValueOf(obj)
-	if !(objT.Kind() == reflect.Ptr && objT.Elem().Kind() == reflect.Struct) {
+	if !isStructPtr(objT) {
 		return fmt.Errorf("%v must be  a struct pointer", obj)
 	}
 	objT = objT.Elem()
@@ -189,8 +189,8 @@ func ParseForm(form url.Values, obj interface{}) error {
 		if !fieldV.CanSet() {
 			continue
 		}
-		fieldT := objT.Field(i)
 
+		fieldT := objT.Field(i)
 		tags := strings.Split(fieldT.Tag.Get("form"), ",")
 		var tag string
 		if len(tags) == 0 || len(tags[0]) == 0 {
@@ -238,6 +238,69 @@ func ParseForm(form url.Values, obj interface{}) error {
 	return nil
 }
 
+// form types for RenderForm function
+var FormType = map[string]bool{
+	"text":     true,
+	"textarea": true,
+	"hidden":   true,
+	"password": true,
+}
+
+var unKind = map[reflect.Kind]bool{
+	reflect.Uintptr:       true,
+	reflect.Complex64:     true,
+	reflect.Complex128:    true,
+	reflect.Array:         true,
+	reflect.Chan:          true,
+	reflect.Func:          true,
+	reflect.Map:           true,
+	reflect.Ptr:           true,
+	reflect.Slice:         true,
+	reflect.Struct:        true,
+	reflect.UnsafePointer: true,
+}
+
+// obj must be a struct pointer
+func RenderForm(obj interface{}) template.HTML {
+	objT := reflect.TypeOf(obj)
+	objV := reflect.ValueOf(obj)
+	if !isStructPtr(objT) {
+		return template.HTML("")
+	}
+	objT = objT.Elem()
+	objV = objV.Elem()
+
+	var raw []string
+	for i := 0; i < objT.NumField(); i++ {
+		fieldV := objV.Field(i)
+		if !fieldV.CanSet() || unKind[fieldV.Kind()] {
+			continue
+		}
+
+		fieldT := objT.Field(i)
+		tags := strings.Split(fieldT.Tag.Get("form"), ",")
+		name := fieldT.Name
+		if len(tags) < 2 {
+			if len(tags) == 1 && len(tags[0]) > 0 {
+				name = tags[0]
+			}
+			raw = append(raw, fmt.Sprintf(`%v: `,
+				fieldT.Name, name, fieldV.Interface()))
+		} else {
+			if len(tags[0]) > 0 {
+				name = tags[0]
+			}
+			raw = append(raw, fmt.Sprintf(`%v: `,
+				fieldT.Name, name, tags[1], fieldV.Interface()))
+		}
+	}
+	return template.HTML(strings.Join(raw, ""))
+}
+
+func isStructPtr(t reflect.Type) bool {
+	return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
+}
+
 func stringsToJson(str string) string {
 	rs := []rune(str)
 	jsons := ""
diff --git a/utils_test.go b/utils_test.go
index 405c939c..994846fe 100644
--- a/utils_test.go
+++ b/utils_test.go
@@ -1,6 +1,7 @@
 package beego
 
 import (
+	"html/template"
 	"net/url"
 	"testing"
 	"time"
@@ -144,3 +145,29 @@ func TestParseForm(t *testing.T) {
 		t.Errorf("Intro should equal `I am an engineer!` but got `%v`", u.Intro)
 	}
 }
+
+func TestRenderForm(t *testing.T) {
+	type user struct {
+		Id    int
+		tag   string      `form:tag`
+		Name  interface{} `form:"username"`
+		Age   int         `form:"age,text"`
+		Email []string
+		Intro string `form:",textarea"`
+	}
+
+	u := user{Name: "test"}
+	output := RenderForm(u)
+	if output != template.HTML("") {
+		t.Errorf("output should be empty but got %v", output)
+	}
+	output = RenderForm(&u)
+	result := template.HTML(
+		`Id: ` +
+			`Name: ` +
+			`Age: ` +
+			`Intro: `)
+	if output != result {
+		t.Errorf("output should equal `%v` but got `%v`", result, output)
+	}
+}