diff --git a/cmd/component.go b/cmd/component.go
index 9fae44c9bc01f5e9960d12f1438e271c04a9857c..fb9103053baf30be98b70baf491fc307aee2352b 100644
--- a/cmd/component.go
+++ b/cmd/component.go
@@ -28,6 +28,9 @@ func init() {
 	RootCmd.AddCommand(componentCmd)
 
 	componentCmd.AddCommand(componentListCmd)
+	componentCmd.AddCommand(componentRmCmd)
+
+	componentRmCmd.PersistentFlags().String(flagComponent, "", "The component to be removed from components/")
 }
 
 var componentCmd = &cobra.Command{
@@ -62,3 +65,25 @@ The ` + "`list`" + ` command displays all known components.
 # List all components
 ks component list`,
 }
+
+var componentRmCmd = &cobra.Command{
+	Use:   "rm <component-name>",
+	Short: "Delete a component from the ksonnet application",
+	RunE: func(cmd *cobra.Command, args []string) error {
+		if len(args) != 1 {
+			return fmt.Errorf("'component rm' takes a single argument, that is the name of the component")
+		}
+
+		component := args[0]
+
+		c := kubecfg.NewComponentRmCmd(component)
+		return c.Run()
+	},
+	Long: `Delete a component from the ksonnet application. This is equivalent to deleting the
+component file in the components directory and cleaning up all component
+references throughout the project.`,
+	Example: `# Remove the component 'guestbook'. This is equivalent to deleting guestbook.jsonnet
+# in the components directory, and cleaning up references to the component
+# throughout the ksonnet application.
+ks component rm guestbook`,
+}
diff --git a/docs/cli-reference/ks_component.md b/docs/cli-reference/ks_component.md
index 2a860d9dd9fa8b7ab7e7f745dd6960ef54624ce5..1ccdd345fa30debe7cf28a978a0a31598549a954 100644
--- a/docs/cli-reference/ks_component.md
+++ b/docs/cli-reference/ks_component.md
@@ -20,4 +20,5 @@ ks component
 ### SEE ALSO
 * [ks](ks.md)	 - Configure your application to deploy to a Kubernetes cluster
 * [ks component list](ks_component_list.md)	 - List known components
+* [ks component rm](ks_component_rm.md)	 - Delete a component from the ksonnet application
 
diff --git a/docs/cli-reference/ks_component_rm.md b/docs/cli-reference/ks_component_rm.md
new file mode 100644
index 0000000000000000000000000000000000000000..f5664af2fb925702e7eb8df25ebde9824e0d2972
--- /dev/null
+++ b/docs/cli-reference/ks_component_rm.md
@@ -0,0 +1,39 @@
+## ks component rm
+
+Delete a component from the ksonnet application
+
+### Synopsis
+
+
+Delete a component from the ksonnet application. This is equivalent to deleting the
+component file in the components directory and cleaning up all component
+references throughout the project.
+
+```
+ks component rm <component-name>
+```
+
+### Examples
+
+```
+# Remove the component 'guestbook'. This is equivalent to deleting guestbook.jsonnet
+# in the components directory, and cleaning up references to the component
+# throughout the ksonnet application.
+ks component rm guestbook
+```
+
+### Options
+
+```
+      --component string   The component to be removed from components/
+```
+
+### Options inherited from parent commands
+
+```
+  -v, --verbose count[=-1]   Increase verbosity. May be given multiple times.
+```
+
+### SEE ALSO
+* [ks component](ks_component.md)	 - Manage ksonnet components
+
diff --git a/metadata/component.go b/metadata/component.go
index e8a6f6611d65e87afbc5949cbc42428d658f3455..7cbe06ebf768608ca6afe3775195770b9c47b94a 100644
--- a/metadata/component.go
+++ b/metadata/component.go
@@ -94,3 +94,107 @@ func (m *manager) CreateComponent(name string, text string, params param.Params,
 	log.Debugf("Writing component parameters at '%s/%s", componentsDir, name)
 	return m.writeComponentParams(name, params)
 }
+
+// DeleteComponent removes the component file and all references.
+// Write operations will happen at the end to minimalize failures that leave
+// the directory structure in a half-finished state.
+func (m *manager) DeleteComponent(name string) error {
+	componentPath, err := m.findComponentPath(name)
+	if err != nil {
+		return err
+	}
+
+	// Build the new component/params.libsonnet file.
+	componentParamsFile, err := afero.ReadFile(m.appFS, string(m.componentParamsPath))
+	if err != nil {
+		return err
+	}
+	componentJsonnet, err := param.DeleteComponent(name, string(componentParamsFile))
+	if err != nil {
+		return err
+	}
+
+	// Build the new environment/<env>/params.libsonnet files.
+	// environment name -> jsonnet
+	envJsonnets := make(map[string]string)
+	envs, err := m.GetEnvironments()
+	if err != nil {
+		return err
+	}
+	for _, env := range envs {
+		path := appendToAbsPath(m.environmentsPath, env.Name, paramsFileName)
+		envParamsFile, err := afero.ReadFile(m.appFS, string(path))
+		if err != nil {
+			return err
+		}
+		jsonnet, err := param.DeleteEnvironmentComponent(name, string(envParamsFile))
+		if err != nil {
+			return err
+		}
+		envJsonnets[env.Name] = jsonnet
+	}
+
+	//
+	// Delete the component references.
+	//
+	log.Infof("Removing component parameter references ...")
+
+	// Remove the references in component/params.libsonnet.
+	log.Debugf("... deleting references in %s", m.componentParamsPath)
+	err = afero.WriteFile(m.appFS, string(m.componentParamsPath), []byte(componentJsonnet), defaultFilePermissions)
+	if err != nil {
+		return err
+	}
+	// Remove the component references in each environment's
+	// environment/<env>/params.libsonnet.
+	for _, env := range envs {
+		path := appendToAbsPath(m.environmentsPath, env.Name, paramsFileName)
+		log.Debugf("... deleting references in %s", path)
+		err = afero.WriteFile(m.appFS, string(path), []byte(envJsonnets[env.Name]), defaultFilePermissions)
+		if err != nil {
+			return err
+		}
+	}
+
+	//
+	// Delete the component file in components/.
+	//
+	log.Infof("Deleting component '%s' at path '%s'", name, componentPath)
+	if err := m.appFS.Remove(componentPath); err != nil {
+		return err
+	}
+
+	// TODO: Remove,
+	// references in main.jsonnet.
+	// component references in other component files (feature does not yet exist).
+	log.Infof("Succesfully deleted component '%s'", name)
+	return nil
+}
+
+func (m *manager) findComponentPath(name string) (string, error) {
+	componentPaths, err := m.ComponentPaths()
+	if err != nil {
+		log.Debugf("Failed to retrieve component paths")
+		return "", err
+	}
+
+	var componentPath string
+	for _, p := range componentPaths {
+		fileName := path.Base(p)
+		component := strings.TrimSuffix(fileName, path.Ext(fileName))
+
+		if component == name {
+			// need to make sure we don't have multiple files with the same component name
+			if componentPath != "" {
+				return "", fmt.Errorf("Found multiple component files with component name '%s'", name)
+			}
+			componentPath = p
+		}
+	}
+
+	if componentPath == "" {
+		return "", fmt.Errorf("No component with name '%s' found", name)
+	}
+
+	return componentPath, nil
+}
diff --git a/metadata/component_test.go b/metadata/component_test.go
index c4a244a507b125f97e2c57454c6128323c5eef13..00a5ddb299fdaa46b4f68f31111714216f3e8dd7 100644
--- a/metadata/component_test.go
+++ b/metadata/component_test.go
@@ -17,6 +17,7 @@ package metadata
 import (
 	"fmt"
 	"os"
+	"path"
 	"sort"
 	"strings"
 	"testing"
@@ -121,3 +122,19 @@ func TestGetAllComponents(t *testing.T) {
 		t.Fatalf("Expected component %s, got %s", expected2, components)
 	}
 }
+
+func TestFindComponentPath(t *testing.T) {
+	m := populateComponentPaths(t)
+	defer cleanComponentPaths(t)
+
+	component := strings.TrimSuffix(componentFile1, path.Ext(componentFile1))
+	expected := fmt.Sprintf("%s/components/%s", componentsPath, componentFile1)
+	path, err := m.findComponentPath(component)
+	if err != nil {
+		t.Fatalf("Failed to find component path, %v", err)
+	}
+
+	if path != expected {
+		t.Fatalf("m.findComponentPath failed; expected '%s', got '%s'", expected, path)
+	}
+}
diff --git a/metadata/interface.go b/metadata/interface.go
index 90c1b7461da28c417dfb1452c3f60b37d3881cc2..f0ae872226113bdd400f7b9d7945fb3a4713b14e 100644
--- a/metadata/interface.go
+++ b/metadata/interface.go
@@ -52,6 +52,7 @@ type Manager interface {
 	ComponentPaths() (AbsPaths, error)
 	GetAllComponents() ([]string, error)
 	CreateComponent(name string, text string, params param.Params, templateType prototype.TemplateType) error
+	DeleteComponent(name string) error
 
 	// Params API.
 	SetComponentParams(component string, params param.Params) error
diff --git a/metadata/params/interface.go b/metadata/params/interface.go
index f33fd1535e4538397d6fbf122684e9deef4a9c0e..c773b5fd441f777c45bfb7125d0cff2dbbd583be 100644
--- a/metadata/params/interface.go
+++ b/metadata/params/interface.go
@@ -28,6 +28,16 @@ func AppendComponent(component, snippet string, params Params) (string, error) {
 	return appendComponent(component, snippet, params)
 }
 
+// DeleteComponent takes
+//
+//   component: the name of the component to be deleted.
+//   snippet: a jsonnet snippet resembling the current component parameters.
+//
+// and returns the jsonnet snippet with the removed component.
+func DeleteComponent(component, snippet string) (string, error) {
+	return deleteComponent(component, snippet)
+}
+
 // GetComponentParams takes
 //
 //  component: the name of the component to retrieve params for.
@@ -82,3 +92,16 @@ func GetAllEnvironmentParams(snippet string) (map[string]Params, error) {
 func SetEnvironmentParams(component, snippet string, params Params) (string, error) {
 	return setEnvironmentParams(component, snippet, params)
 }
+
+// DeleteEnvironmentComponent takes
+//
+//   component: the name of the component to be deleted.
+//   snippet: a jsonnet snippet resembling the current environment parameters (not expanded).
+//
+// and returns the jsonnet snippet with the removed component.
+func DeleteEnvironmentComponent(component, snippet string) (string, error) {
+	// The implementation happens to be the same as DeleteComponent, but we're
+	// keeping the two interfaces separate since we're fundamentally operating
+	// on two different jsonnet schemas.
+	return deleteComponent(component, snippet)
+}
diff --git a/metadata/params/params.go b/metadata/params/params.go
index 8b2d948c28c6615c7cae96cbc8d99c1cb23e74aa..20a10ab105d90587396cbfd062cf08120e8f9570 100644
--- a/metadata/params/params.go
+++ b/metadata/params/params.go
@@ -194,6 +194,33 @@ func writeParams(indent int, params Params) string {
 	return buffer.String()
 }
 
+func deleteComponent(component, snippet string) (string, error) {
+	componentsNode, err := componentsObj(component, snippet)
+	if err != nil {
+		return "", err
+	}
+
+	for _, field := range componentsNode.Fields {
+		hasComponent, err := hasComponent(component, field)
+		if err != nil {
+			return "", err
+		}
+		if hasComponent {
+			lines := strings.Split(snippet, "\n")
+
+			removeLineBegin := field.Expr2.Loc().Begin.Line - 1
+			removeLineEnd := field.Expr2.Loc().End.Line
+
+			lines = append(lines[:removeLineBegin], lines[removeLineEnd:]...)
+
+			return strings.Join(lines, "\n"), nil
+		}
+	}
+
+	// No component references, just return the original snippet.
+	return snippet, nil
+}
+
 // ---------------------------------------------------------------------------
 // Component Parameter-specific functionality
 
diff --git a/metadata/params/params_test.go b/metadata/params/params_test.go
index f7feeca848e1e39ada5a64179be272a6f9c5124f..81c4cc5c08a09834888910c8a70955a0b7e5c9d3 100644
--- a/metadata/params/params_test.go
+++ b/metadata/params/params_test.go
@@ -242,6 +242,209 @@ local bar = import "bar";
 	}
 }
 
+func TestDeleteComponent(t *testing.T) {
+	tests := []struct {
+		componentName string
+		jsonnet       string
+		expected      string
+	}{
+		// Test case with existing component
+		{
+			"bar",
+			`
+{
+  components: {
+    foo: {
+      name: "foo",
+      replicas: 1,
+    },
+    bar: {
+      name: "bar",
+    },
+  },
+}`,
+			`
+{
+  components: {
+    foo: {
+      name: "foo",
+      replicas: 1,
+    },
+  },
+}`,
+		},
+		// Test another case with existing component
+		{
+			"bar",
+			`
+{
+  components: {
+    bar: {
+      name: "bar",
+    },
+  },
+}`,
+			`
+{
+  components: {
+  },
+}`,
+		},
+		// Test case where component doesn't exist
+		{
+			"bar",
+			`
+{
+  components: {
+    foo: {
+      name: "foo",
+      replicas: 1,
+    },
+  },
+}`,
+			`
+{
+  components: {
+    foo: {
+      name: "foo",
+      replicas: 1,
+    },
+  },
+}`,
+		},
+	}
+
+	for _, s := range tests {
+		parsed, err := DeleteComponent(s.componentName, s.jsonnet)
+		if err != nil {
+			t.Errorf("Unexpected error\n  input: %v\n  error: %v", s.jsonnet, err)
+		}
+
+		if parsed != s.expected {
+			t.Errorf("Wrong conversion\n  expected: %v\n  got: %v", s.expected, parsed)
+		}
+	}
+}
+
+func TestDeleteEnvironmentComponent(t *testing.T) {
+	tests := []struct {
+		componentName string
+		jsonnet       string
+		expected      string
+	}{
+		// Test case with existing component
+		{
+			"bar",
+			`
+local params = import "/fake/path";
+params + {
+  components +: {
+    bar +: {
+      name: "bar",
+      "replica-count": 1,
+    },
+    foo +: {
+      name: "foo",
+    },
+  },
+}`,
+			`
+local params = import "/fake/path";
+params + {
+  components +: {
+    foo +: {
+      name: "foo",
+    },
+  },
+}`,
+		},
+		// Test another case with existing component
+		{
+			"foo",
+			`
+local params = import "/fake/path";
+params + {
+  components +: {
+    bar +: {
+      name: "bar",
+      "replica-count": 1,
+    },
+    foo +: {
+      name: "foo",
+    },
+  },
+}`,
+			`
+local params = import "/fake/path";
+params + {
+  components +: {
+    bar +: {
+      name: "bar",
+      "replica-count": 1,
+    },
+  },
+}`,
+		},
+		// Test case where component doesn't exist
+		{
+			"baz",
+			`
+local params = import "/fake/path";
+params + {
+  components +: {
+    bar +: {
+      name: "bar",
+      "replica-count": 1,
+    },
+    foo +: {
+      name: "foo",
+    },
+  },
+}`,
+			`
+local params = import "/fake/path";
+params + {
+  components +: {
+    bar +: {
+      name: "bar",
+      "replica-count": 1,
+    },
+    foo +: {
+      name: "foo",
+    },
+  },
+}`,
+		},
+		// Test case where there are no components
+		{
+			"baz",
+			`
+local params = import "/fake/path";
+params + {
+  components +: {
+  },
+}`,
+			`
+local params = import "/fake/path";
+params + {
+  components +: {
+  },
+}`,
+		},
+	}
+
+	for _, s := range tests {
+		parsed, err := DeleteEnvironmentComponent(s.componentName, s.jsonnet)
+		if err != nil {
+			t.Errorf("Unexpected error\n  input: %v\n  error: %v", s.jsonnet, err)
+		}
+
+		if parsed != s.expected {
+			t.Errorf("Wrong conversion\n  expected: %v\n  got: %v", s.expected, parsed)
+		}
+	}
+}
+
 func TestGetComponentParams(t *testing.T) {
 	tests := []struct {
 		componentName string
diff --git a/pkg/kubecfg/component.go b/pkg/kubecfg/component.go
index cdb16d93c7e9b8ae533fb9a45c7b5f00c3370a35..0c4fb9e994f8c0664f3bc114c39917c741504043 100644
--- a/pkg/kubecfg/component.go
+++ b/pkg/kubecfg/component.go
@@ -53,6 +53,27 @@ func (c *ComponentListCmd) Run(out io.Writer) error {
 	return err
 }
 
+// ComponentRmCmd stores the information necessary to remove a component from
+// the ksonnet application.
+type ComponentRmCmd struct {
+	component string
+}
+
+// NewComponentRmCmd acts as a constructor for ComponentRmCmd.
+func NewComponentRmCmd(component string) *ComponentRmCmd {
+	return &ComponentRmCmd{component: component}
+}
+
+// Run executes the removing of the component.
+func (c *ComponentRmCmd) Run() error {
+	manager, err := manager()
+	if err != nil {
+		return err
+	}
+
+	return manager.DeleteComponent(c.component)
+}
+
 func printComponents(out io.Writer, components []string) (string, error) {
 	rows := [][]string{
 		[]string{componentNameHeader},