Skip to content

Add comprehensive login page customization options #7374

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions docs/FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,23 @@ You can change the config file's location using the `--config` flag or

The default location respects `$XDG_CONFIG_HOME`.

### Login page customization

You can customize the login page using the `--custom-strings` flag:

```bash
code-server --custom-strings '{"LOGIN_TITLE": "My Code Server", "WELCOME": "Welcome to my portal"}'
```

Or use a JSON file:
```bash
code-server --custom-strings /path/to/custom-strings.json
```

Legacy individual flags (`--app-name`, `--welcome-text`) are still supported but deprecated.

For detailed customization options and examples, see the [customization guide](./customization.md).

## How do I make my keyboard shortcuts work?

Many shortcuts will not work by default, since they'll be "caught" by the browser.
Expand Down
2 changes: 2 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ code-server.
We also have an in-depth [setup and
configuration](https://coder.com/docs/code-server/latest/guide) guide.

You can also customize the login page appearance - see our [customization guide](./customization.md).

## Questions?

See answers to [frequently asked
Expand Down
170 changes: 170 additions & 0 deletions docs/customization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
# Login Page Customization

code-server allows you to customize the login page appearance and messages through a unified `--custom-strings` flag or legacy CLI arguments.

## Recommended Approach: Custom Strings

The `--custom-strings` flag provides a scalable way to customize any UI text by leveraging the built-in internationalization system.

### Using JSON File

Create a JSON file with your customizations:

```json
{
"WELCOME": "Welcome to {{app}} Development Portal",
"LOGIN_TITLE": "{{app}} Secure Access",
"LOGIN_BELOW": "Please authenticate to continue",
"PASSWORD_PLACEHOLDER": "Enter your access code",
"SUBMIT": "AUTHENTICATE",
"LOGIN_PASSWORD": "Check the config file at {{configFile}} for the password.",
"LOGIN_USING_ENV_PASSWORD": "Access code provided via environment variable",
"LOGIN_USING_HASHED_PASSWORD": "Access code configured securely",
"LOGIN_RATE_LIMIT": "Too many attempts. Please wait before trying again.",
"MISS_PASSWORD": "Access code is required",
"INCORRECT_PASSWORD": "Invalid access code"
}
```

```bash
code-server --custom-strings /path/to/custom-strings.json
```

### Using Inline JSON

```bash
code-server --custom-strings '{"WELCOME": "Welcome to My Dev Portal", "LOGIN_TITLE": "Development Access", "SUBMIT": "SIGN IN"}'
```

### Configuration File

Add to your `~/.config/code-server/config.yaml`:

```yaml
bind-addr: 127.0.0.1:8080
auth: password
password: your-password
custom-strings: |
{
"WELCOME": "Welcome to {{app}} Development Portal",
"LOGIN_TITLE": "{{app}} Secure Access",
"PASSWORD_PLACEHOLDER": "Enter your access code",
"SUBMIT": "AUTHENTICATE"
}
```

## Available Customization Keys

| Key | Description | Default | Supports {{app}} placeholder |
|-----|-------------|---------|------------------------------|
| `WELCOME` | Welcome message on login page | "Welcome to {{app}}" | βœ… |
| `LOGIN_TITLE` | Main title on login page | "{{app}} login" | βœ… |
| `LOGIN_BELOW` | Text below the login title | "Please log in below." | ❌ |
| `PASSWORD_PLACEHOLDER` | Password field placeholder text | "PASSWORD" | ❌ |
| `SUBMIT` | Login button text | "SUBMIT" | ❌ |
| `LOGIN_PASSWORD` | Message for config file password | "Check the config file at {{configFile}} for the password." | ❌ |
| `LOGIN_USING_ENV_PASSWORD` | Message when using `$PASSWORD` env var | "Password was set from $PASSWORD." | ❌ |
| `LOGIN_USING_HASHED_PASSWORD` | Message when using `$HASHED_PASSWORD` env var | "Password was set from $HASHED_PASSWORD." | ❌ |
| `LOGIN_RATE_LIMIT` | Rate limiting error message | "Login rate limited!" | ❌ |
| `MISS_PASSWORD` | Empty password error message | "Missing password" | ❌ |
| `INCORRECT_PASSWORD` | Wrong password error message | "Incorrect password" | ❌ |

## Docker Examples

### Basic Docker Deployment

```bash
docker run -it --name code-server -p 127.0.0.1:8080:8080 \
-v "$PWD:/home/coder/project" \
-v "$PWD/custom-strings.json:/custom-strings.json" \
codercom/code-server:latest --custom-strings /custom-strings.json
```

### Corporate Branding with Inline JSON

```bash
docker run -it --name code-server -p 127.0.0.1:8080:8080 \
-v "$PWD:/home/coder/project" \
codercom/code-server:latest --custom-strings '{
"WELCOME": "Welcome to ACME Corporation Development Portal",
"LOGIN_TITLE": "ACME Dev Portal Access",
"LOGIN_BELOW": "Enter your corporate credentials",
"PASSWORD_PLACEHOLDER": "Corporate Password",
"SUBMIT": "SIGN IN",
"LOGIN_USING_ENV_PASSWORD": "Password managed by IT department"
}'
```

## Legacy Support (Deprecated)

The following flags are still supported but deprecated. Use `--custom-strings` for new deployments:

```bash
# Deprecated - use --custom-strings instead
code-server \
--app-name "My Development Server" \
--welcome-text "Welcome to the development environment"
```

These legacy flags will show deprecation warnings and may be removed in future versions.

## Migration Guide

### From Legacy Flags to Custom Strings

**Old approach:**
```bash
code-server \
--app-name "Dev Portal" \
--welcome-text "Welcome to development"
```

**New approach:**
```bash
code-server --custom-strings '{
"WELCOME": "Welcome to development"
}'
```

**Note:** The `--app-name` flag controls the `{{app}}` placeholder in templates. You can either:
1. Keep using `--app-name` alongside `--custom-strings`
2. Customize the full text without placeholders in your JSON

## Benefits of Custom Strings

- βœ… **Scalable**: Add any new UI strings without new CLI flags
- βœ… **Flexible**: Supports both files and inline JSON
- βœ… **Future-proof**: Automatically supports new UI strings as they're added
- βœ… **Organized**: All customizations in one place
- βœ… **Version-controlled**: JSON files can be tracked in your repository

## Advanced Usage

### Multi-language Support

Create different JSON files for different languages:

```bash
# English
code-server --custom-strings /config/strings-en.json

# Spanish
code-server --custom-strings /config/strings-es.json --locale es
```

### Dynamic Customization

Generate JSON dynamically in scripts:

```bash
#!/bin/bash
COMPANY_NAME="ACME Corp"
cat > /tmp/strings.json << EOF
{
"WELCOME": "Welcome to ${COMPANY_NAME} Development Portal",
"LOGIN_TITLE": "${COMPANY_NAME} Access Portal"
}
EOF

code-server --custom-strings /tmp/strings.json
```
28 changes: 28 additions & 0 deletions docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,34 @@ docker run -it --name code-server -p 127.0.0.1:8080:8080 \
codercom/code-server:latest
```

### Customizing the login page

You can customize the login page using the `--custom-strings` flag:

```bash
# Example with inline JSON customization
docker run -it --name code-server -p 127.0.0.1:8080:8080 \
-v "$PWD:/home/coder/project" \
codercom/code-server:latest --custom-strings '{
"LOGIN_TITLE": "My Development Environment",
"WELCOME": "Welcome to your coding workspace",
"PASSWORD_PLACEHOLDER": "Enter your secure password",
"SUBMIT": "ACCESS"
}'
```

Or mount a JSON file:

```bash
# Example with JSON file
docker run -it --name code-server -p 127.0.0.1:8080:8080 \
-v "$PWD:/home/coder/project" \
-v "$PWD/custom-strings.json:/config/strings.json" \
codercom/code-server:latest --custom-strings /config/strings.json
```

For detailed customization options, see the [customization guide](./customization.md).

Our official image supports `amd64` and `arm64`. For `arm32` support, you can
use a [community-maintained code-server
alternative](https://hub.docker.com/r/linuxserver/code-server).
Expand Down
23 changes: 23 additions & 0 deletions src/node/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export interface UserProvidedArgs extends UserProvidedCodeArgs {
"app-name"?: string
"welcome-text"?: string
"abs-proxy-base-path"?: string
"custom-strings"?: string
/* Positional arguments. */
_?: string[]
}
Expand Down Expand Up @@ -285,16 +286,22 @@ export const options: Options<Required<UserProvidedArgs>> = {
type: "string",
short: "an",
description: "The name to use in branding. Will be shown in titlebar and welcome message",
deprecated: true,
},
"welcome-text": {
type: "string",
short: "w",
description: "Text to show on login page",
deprecated: true,
},
"abs-proxy-base-path": {
type: "string",
description: "The base path to prefix to all absproxy requests",
},
"custom-strings": {
type: "string",
description: "Path to JSON file or raw JSON string with custom UI strings. Merges with default strings and supports all i18n keys.",
},
}

export const optionDescriptions = (opts: Partial<Options<Required<UserProvidedArgs>>> = options): string[] => {
Expand Down Expand Up @@ -459,6 +466,21 @@ export const parse = (
throw new Error("--cert-key is missing")
}

// Validate custom-strings flag
if (args["custom-strings"]) {
try {
// First try to parse as JSON directly
JSON.parse(args["custom-strings"])
} catch (jsonError) {
// If JSON parsing fails, check if it's a valid file path
if (!args["custom-strings"].startsWith("{") && !args["custom-strings"].startsWith("[")) {
// Assume it's a file path - validation will happen later when the file is read
} else {
throw error(`--custom-strings contains invalid JSON: ${jsonError instanceof Error ? jsonError.message : String(jsonError)}`)
}
}
}

logger.debug(() => [`parsed ${opts?.configFile ? "config" : "command line"}`, field("args", redactArgs(args))])

return args
Expand Down Expand Up @@ -593,6 +615,7 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config
args["disable-proxy"] = true
}


const usingEnvHashedPassword = !!process.env.HASHED_PASSWORD
if (process.env.HASHED_PASSWORD) {
args["hashed-password"] = process.env.HASHED_PASSWORD
Expand Down
Loading